[
  {
    "path": ".gitignore",
    "content": ".gradle\ngradle.properties\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\nbin\ntmp\ngen\n\n#################\n## #IntelliJ IDEA\n#################\n.idea\n*.iml\n*.ipr\n*.iws\nout\n\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.metadata\n*.tmp\n*.bak\n*.swp\n*~.nib\nlocal.properties\n.settings/\n.loadpath\n*.classpath\n*.project\nREADME.md~\n\n/releaseToBintray.txt\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n"
  },
  {
    "path": "README.md",
    "content": "# LitePal for Android  \n![Logo](https://github.com/LitePalFramework/LitePal/blob/master/sample/src/main/logo/mini_logo.png) \n\n[中文文档](https://blog.csdn.net/sinyu890807/category_9262963.html)\n\nLitePal is an open source Android library that allows developers to use SQLite database extremely easy. You can finish most of the database operations without writing even a SQL statement, including create or upgrade tables, crud operations, aggregate functions, etc. The setup of LitePal is quite simple as well, you can integrate it into your project in less than 5 minutes. \n\nExperience the magic right now and have fun!\n\n## Features\n * Using object-relational mapping (ORM) pattern.\n * Almost zero-configuration(only one configuration file with few properties).\n * Maintains all tables automatically(e.g. create, alter or drop tables).\n * Multi databases supported.\n * Encapsulated APIs for avoiding writing SQL statements.\n * Awesome fluent query API.\n * Alternative choice to use SQL still, but easier and better APIs than the originals.\n * More for you to explore.\n\n## Quick Setup\n#### 1. Include library\n\nEdit your **build.gradle** file and add below dependency.\n\n``` groovy\ndependencies {\n    implementation 'org.litepal.guolindev:core:3.2.3'\n}\n```\n\n#### 2. Configure litepal.xml\nCreate a file in the **assets** folder of your project and name it as **litepal.xml**. Then copy the following codes into it.\n``` xml\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<litepal>\n    <!--\n    \tDefine the database name of your application. \n    \tBy default each database name should be end with .db. \n    \tIf you didn't name your database end with .db, \n    \tLitePal would plus the suffix automatically for you.\n    \tFor example:    \n    \t<dbname value=\"demo\" />\n    -->\n    <dbname value=\"demo\" />\n\n    <!--\n    \tDefine the version of your database. Each time you want \n    \tto upgrade your database, the version tag would helps.\n    \tModify the models you defined in the mapping tag, and just \n    \tmake the version value plus one, the upgrade of database\n    \twill be processed automatically without concern.\n\t\t\tFor example:    \n    \t<version value=\"1\" />\n    -->\n    <version value=\"1\" />\n\n    <!--\n    \tDefine your models in the list with mapping tag, LitePal will\n    \tcreate tables for each mapping class. The supported fields\n    \tdefined in models will be mapped into columns.\n    \tFor example:    \n    \t<list>\n    \t\t<mapping class=\"com.test.model.Reader\" />\n    \t\t<mapping class=\"com.test.model.Magazine\" />\n    \t</list>\n    -->\n    <list>\n    </list>\n    \n    <!--\n        Define where the .db file should be. \"internal\" means the .db file\n        will be stored in the database folder of internal storage which no\n        one can access. \"external\" means the .db file will be stored in the\n        path to the directory on the primary external storage device where\n        the application can place persistent files it owns which everyone\n        can access. \"internal\" will act as default.\n        For example:\n        <storage value=\"external\" />\n    -->\n    \n</litepal>\n```\nThis is the only configuration file, and the properties are simple. \n * **dbname** configure the database name of project.\n * **version** configure the version of database. Each time you want to upgrade database, plus the value here.\n * **list** configure the mapping classes.\n * **storage** configure where the database file should be stored. **internal** and **external** are the only valid options.\n \n#### 3. Configure LitePalApplication\nYou don't want to pass the Context param all the time. To makes the APIs simple, just configure the LitePalApplication in **AndroidManifest.xml** as below:\n``` xml\n<manifest>\n    <application\n        android:name=\"org.litepal.LitePalApplication\"\n        ...\n    >\n        ...\n    </application>\n</manifest>\n```\nOf course you may have your own Application and has already configured here, like:\n``` xml\n<manifest>\n    <application\n        android:name=\"com.example.MyOwnApplication\"\n        ...\n    >\n        ...\n    </application>\n</manifest>\n```\nThat's OK. LitePal can still live with that. Just call **LitePal.initialize(context)** in your own Application:\n```java\npublic class MyOwnApplication extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        LitePal.initialize(this);\n    }\n    ...\n}\n```\nMake sure to call this method as early as you can. In the **onCreate()** method of Application will be fine. And always remember to use the application context as parameter. Do not use any instance of activity or service as parameter, or memory leaks might happen.\n## Get Started\nAfter setup, you can experience the powerful functions now.\n\n#### 1. Create tables\nDefine the models first. For example you have two models, **Album** and **Song**. The models can be defined as below:\n``` java\npublic class Album extends LitePalSupport {\n\t\n    @Column(unique = true, defaultValue = \"unknown\")\n    private String name;\n    \n    @Column(index = true)\n    private float price;\n\t\n    private List<Song> songs = new ArrayList<>();\n\n    // generated getters and setters.\n    ...\n}\n```\n``` java\npublic class Song extends LitePalSupport {\n\t\n    @Column(nullable = false)\n    private String name;\n\t\n    private int duration;\n\t\n    @Column(ignore = true)\n    private String uselessField;\n\t\n    private Album album;\n\n    // generated getters and setters.\n    ...\n}\n```\nThen add these models into the mapping list in **litepal.xml**:\n``` xml\n<list>\n    <mapping class=\"org.litepal.litepalsample.model.Album\" />\n    <mapping class=\"org.litepal.litepalsample.model.Song\" />\n</list>\n```\nOK! The tables will be generated next time you operate database. For example, gets the **SQLiteDatabase** with following codes:\n``` java\nSQLiteDatabase db = LitePal.getDatabase();\n```\nNow the tables will be generated automatically with SQLs like this:\n``` sql\nCREATE TABLE album (\n\tid integer primary key autoincrement,\n\tname text unique default 'unknown',\n\tprice real\n);\n\nCREATE TABLE song (\n\tid integer primary key autoincrement,\n\tname text not null,\n\tduration integer,\n\talbum_id integer\n);\n```\n\n#### 2. Upgrade tables\nUpgrade tables in LitePal is extremely easy. Just modify your models anyway you want:\n```java\npublic class Album extends LitePalSupport {\n\t\n    @Column(unique = true, defaultValue = \"unknown\")\n    private String name;\n\t\n    @Column(ignore = true)\n    private float price;\n\t\n    private Date releaseDate;\n\t\n    private List<Song> songs = new ArrayList<>();\n\n    // generated getters and setters.\n    ...\n}\n```\nA **releaseDate** field was added and **price** field was annotated to ignore.\nThen increase the version number in **litepal.xml**:\n```xml\n<!--\n    Define the version of your database. Each time you want \n    to upgrade your database, the version tag would helps.\n    Modify the models you defined in the mapping tag, and just \n    make the version value plus one, the upgrade of database\n    will be processed automatically without concern.\n    For example:    \n    <version value=\"1\" />\n-->\n<version value=\"2\" />\n```\nThe tables will be upgraded next time you operate database. A **releasedate** column will be added into **album** table and the original **price** column will be removed. All the data in **album** table except those removed columns will be retained.\n\nBut there are some upgrading conditions that LitePal can't handle and all data in the upgrading table will be cleaned:\n * Add a field which annotated as `unique = true`.\n * Change a field's annotation into `unique = true`.\n * Change a field's annotation into `nullable = false`.\n\nBe careful of the above conditions which will cause losing data.\n\n#### 3. Save data\nThe saving API is quite object oriented. Each model which inherits from **LitePalSupport** would have the **save()** method for free.\n\nJava:\n``` java\nAlbum album = new Album();\nalbum.setName(\"album\");\nalbum.setPrice(10.99f);\nalbum.setCover(getCoverImageBytes());\nalbum.save();\nSong song1 = new Song();\nsong1.setName(\"song1\");\nsong1.setDuration(320);\nsong1.setAlbum(album);\nsong1.save();\nSong song2 = new Song();\nsong2.setName(\"song2\");\nsong2.setDuration(356);\nsong2.setAlbum(album);\nsong2.save();\n```\n\nKotlin:\n```kotlin\nval album = Album()\nalbum.name = \"album\"\nalbum.price = 10.99f\nalbum.cover = getCoverImageBytes()\nalbum.save()\nval song1 = Song()\nsong1.name = \"song1\"\nsong1.duration = 320\nsong1.album = album\nsong1.save()\nval song2 = Song()\nsong2.name = \"song2\"\nsong2.duration = 356\nsong2.album = album\nsong2.save()\n```\nThis will insert album, song1 and song2 into database with associations.\n\n#### 4. Update data\nThe simplest way, use **save()** method to update a record found by **find()**.\n\nJava:\n``` java\nAlbum albumToUpdate = LitePal.find(Album.class, 1);\nalbumToUpdate.setPrice(20.99f); // raise the price\nalbumToUpdate.save();\n```\n\nKotlin:\n```kotlin\nval albumToUpdate = LitePal.find<Album>(1)\nalbumToUpdate.price = 20.99f // raise the price\nalbumToUpdate.save()\n```\n\nEach model which inherits from **LitePalSupport** would also have **update()** and **updateAll()** method. You can update a single record with a specified id.\n\nJava:\n``` java\nAlbum albumToUpdate = new Album();\nalbumToUpdate.setPrice(20.99f); // raise the price\nalbumToUpdate.update(id);\n```\n\nKotlin:\n```kotlin\nval albumToUpdate = Album()\nalbumToUpdate.price = 20.99f // raise the price\nalbumToUpdate.update(id)\n```\n\nOr you can update multiple records with a where condition.\n\nJava:\n``` java\nAlbum albumToUpdate = new Album();\nalbumToUpdate.setPrice(20.99f); // raise the price\nalbumToUpdate.updateAll(\"name = ?\", \"album\");\n```\n\nKotlin:\n```kotlin\nval albumToUpdate = Album()\nalbumToUpdate.price = 20.99f // raise the price\nalbumToUpdate.updateAll(\"name = ?\", \"album\")\n```\n\n#### 5. Delete data\nYou can delete a single record using the static **delete()** method in **LitePal**.\n\nJava:\n``` java\nLitePal.delete(Song.class, id);\n```\n\nKotlin:\n```kotlin\nLitePal.delete<Song>(id)\n```\n\nOr delete multiple records using the static **deleteAll()** method in **LitePal**.\n\nJava:\n``` java\nLitePal.deleteAll(Song.class, \"duration > ?\" , \"350\");\n```\n\nKotlin:\n```kotlin\nLitePal.deleteAll<Song>(\"duration > ?\" , \"350\")\n```\n\n#### 6. Query data\nFind a single record from song table with specified id.\n\nJava:\n``` java\nSong song = LitePal.find(Song.class, id);\n```\n\nKotlin:\n```kotlin\nval song = LitePal.find<Song>(id)\n```\n\nFind all records from song table.\n\nJava:\n``` java\nList<Song> allSongs = LitePal.findAll(Song.class);\n```\n\nKotlin:\n```kotlin\nval allSongs = LitePal.findAll<Song>()\n```\n\nConstructing complex query with fluent query.\n\nJava:\n``` java\nList<Song> songs = LitePal.where(\"name like ? and duration < ?\", \"song%\", \"200\").order(\"duration\").find(Song.class);\n```\n\nKotlin:\n``` kotlin\nval songs = LitePal.where(\"name like ? and duration < ?\", \"song%\", \"200\").order(\"duration\").find<Song>()\n```\n\n#### 7. Multiple databases\nIf your app needs multiple databases, LitePal support it completely. You can create as many databases as you want at runtime. For example:\n```java\nLitePalDB litePalDB = new LitePalDB(\"demo2\", 1);\nlitePalDB.addClassName(Singer.class.getName());\nlitePalDB.addClassName(Album.class.getName());\nlitePalDB.addClassName(Song.class.getName());\nLitePal.use(litePalDB);\n```\nThis will create a **demo2** database with **singer**, **album** and **song** tables.\n\nIf you just want to create a new database but with same configuration as **litepal.xml**, you can do it with:\n```java\nLitePalDB litePalDB = LitePalDB.fromDefault(\"newdb\");\nLitePal.use(litePalDB);\n```\nYou can always switch back to default database with:\n```java\nLitePal.useDefault();\n```\nAnd you can delete any database by specified database name:\n```java\nLitePal.deleteDatabase(\"newdb\");\n```\n\n#### 8. Transaction\nLitePal support transaction for atomic db operations. All operations in the transaction will be committed or rolled back together.\n\nJava usage:\n```java\nLitePal.beginTransaction();\nboolean result1 = // db operation1\nboolean result2 = // db operation2\nboolean result3 = // db operation3\nif (result1 && result2 && result3) {\n    LitePal.setTransactionSuccessful();\n}\nLitePal.endTransaction();\n```\n\nKotlin usage:\n```kotlin\nLitePal.runInTransaction {\n    val result1 = // db operation1\n    val result2 = // db operation2\n    val result3 = // db operation3\n    result1 && result2 && result3\n}\n```\n\n## ProGuard\nIf you are using ProGuard you might need to add the following option:\n\n```proguard\n-keep class org.litepal.** {*;}\n-keep class * extends org.litepal.crud.LitePalSupport {*;}\n```\n\n## Bugs Report\nIf you find any bug when using LitePal, please report **[here](https://github.com/LitePalFramework/LitePal/issues/new)**. Thanks for helping us making better.\n\n## Change logs\n\n### 3.2.3\n * Support database index by adding @Column(index = true) on field.\n * Adding return value for **runInTransaction()** function for Kotlin.\n * Fix known bugs.\n\n### 3.1.1\n * Support transaction.\n * Add return value for **LitePal.saveAll()** method.\n * No longer support byte array field as column in table.\n * Deprecate all async methods. You should handle async operations by yourself.\n * Fix known bugs.\n \n### 3.0.0\n * Optimize generic usage for async operation APIs.\n * Add **LitePal.registerDatabaseListener()** method for listening create or upgrade database events.\n * Provider better CRUD API usage for using generic declaration instead of Class parameter for kotlin.\n * Fix known bugs.\n\n### 2.0.0\n * Offer new APIs for CRUD operations. Deprecate **DataSupport**, use **LitePal** and **LitePalSupport** instead.\n * Fully support kotlin programming.\n * Fix known bugs.\n\n### 1.6.1\n * Support AES and MD5 encryption with @Encrypt annotation on fields.\n * Support to store database file on any directory of external storage.\n * Fix known bugs.\n\n### 1.5.1\n * Support async operations for all crud methods.\n * Add **saveOrUpdate()** method in DataSupport.\n * Fix known bugs.\n\n### 1.4.1\n * Support multiple databases.\n * Support crud operations for generic collection data in models.\n * Add SQLite keywords convert function to avoid keywords conflict.\n * Fix bug of DateSupport.count error.\n * Fix bug of losing blob data when upgrading database.\n * Fix other known bugs.\n \n### 1.3.2\n * Improve an outstanding speed up of querying and saving.\n * Support to store database file in external storage.\n * Support to mapping fields which inherit from superclass.\n * Add **findFirst()** and **findLast()** in fluent query.\n * Add **isExist()** and **saveIfNotExist()** method in DataSupport.\n\n### 1.3.1\n * Support storing binary data. Byte array field will be mapped into database as blob type.\n * Add **saveFast()** method in DataSupport. If your model has no associations to handle, use **saveFast()** method will be much more efficient.\n * Improve query speed with optimized algorithm.\n \n### 1.3.0\n * Add annotation functions to declare **unique**, **not null** and **default** constraints.\n * Remove the trick of ignore mapping fields with non-private modifier.\n * Support to use annotation to ignore mapping fields with `ignore = true`\n * Add some magical methods in DataSupport for those who understand LitePal deeper.\n * Fix known bugs.\n \n## License\n```\nCopyright (C) Lin Guo, LitePal Framework Open Source Project\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext.kotlin_version = '1.5.21'\n    repositories {\n        google()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.0.1'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "core/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "core/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\n\nandroid {\n    compileSdkVersion 31\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 31\n        archivesBaseName = \"core\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n}\n\ndependencies {\n    api fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n}"
  },
  {
    "path": "core/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /home/tony/Android/Sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "core/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"org.litepal\">\n\n    <application>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "core/src/main/java/org/litepal/FluentQuery.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal;\n\nimport android.text.TextUtils;\n\nimport org.litepal.crud.LitePalSupport;\nimport org.litepal.crud.QueryHandler;\nimport org.litepal.crud.async.AverageExecutor;\nimport org.litepal.crud.async.CountExecutor;\nimport org.litepal.crud.async.FindExecutor;\nimport org.litepal.crud.async.FindMultiExecutor;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.DBUtility;\n\nimport java.util.List;\n\n/**\n * Allows developers to query tables with fluent style.\n *\n * @author Tony Green\n * @since 2.0\n */\npublic class FluentQuery {\n\n\t/**\n\t * Representing the selected columns in SQL.\n\t */\n\tString[] mColumns;\n\n\t/**\n\t * Representing the where clause in SQL.\n\t */\n\tString[] mConditions;\n\n\t/**\n\t * Representing the order by clause in SQL.\n\t */\n\tString mOrderBy;\n\n\t/**\n\t * Representing the limit clause in SQL.\n\t */\n\tString mLimit;\n\n\t/**\n\t * Representing the offset in SQL.\n\t */\n\tString mOffset;\n\n\t/**\n\t * Do not allow to create instance by developers.\n\t */\n\tFluentQuery() {\n\t}\n\n\t/**\n\t * Declaring to query which columns in table.\n\t *\n\t * <pre>\n\t * LitePal.select(&quot;name&quot;, &quot;age&quot;).find(Person.class);\n\t * </pre>\n\t *\n\t * This will find all rows with name and age columns in Person table.\n\t *\n\t * @param columns\n\t *            A String array of which columns to return. Passing null will\n\t *            return all columns.\n\t *\n\t * @return A ClusterQuery instance.\n\t */\n\tpublic FluentQuery select(String... columns) {\n        mColumns = columns;\n        return this;\n\t}\n\n\t/**\n\t * Declaring to query which rows in table.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;name = ? or age &gt; ?&quot;, &quot;Tom&quot;, &quot;14&quot;).find(Person.class);\n\t * </pre>\n\t *\n\t * This will find rows which name is Tom or age greater than 14 in Person\n\t * table.\n\t *\n\t * @param conditions\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause. Passing null will return all rows.\n\t * @return A ClusterQuery instance.\n\t */\n\tpublic FluentQuery where(String... conditions) {\n        mConditions = conditions;\n        return this;\n\t}\n\n\t/**\n\t * Declaring how to order the rows queried from table.\n\t *\n\t * <pre>\n\t * LitePal.order(&quot;name desc&quot;).find(Person.class);\n\t * </pre>\n\t *\n\t * This will find all rows in Person table sorted by name with inverted\n\t * order.\n\t *\n\t * @param column\n\t *            How to order the rows, formatted as an SQL ORDER BY clause.\n\t *            Passing null will use the default sort order, which may be\n\t *            unordered.\n\t * @return A ClusterQuery instance.\n\t */\n\tpublic FluentQuery order(String column) {\n        mOrderBy = column;\n        return this;\n\t}\n\n\t/**\n\t * Limits the number of rows returned by the query.\n\t *\n\t * <pre>\n\t * LitePal.limit(2).find(Person.class);\n\t * </pre>\n\t *\n\t * This will find the top 2 rows in Person table.\n\t *\n\t * @param value\n\t *            Limits the number of rows returned by the query, formatted as\n\t *            LIMIT clause.\n\t * @return A ClusterQuery instance.\n\t */\n\tpublic FluentQuery limit(int value) {\n        mLimit = String.valueOf(value);\n        return this;\n\t}\n\n\t/**\n\t * Declaring the offset of rows returned by the query. This method must be\n\t * used with {@link #limit(int)}, or nothing will return.\n\t *\n\t * <pre>\n\t * LitePal.limit(1).offset(2).find(Person.class);\n\t * </pre>\n\t *\n\t * This will find the third row in Person table.\n\t *\n\t * @param value\n\t *            The offset amount of rows returned by the query.\n\t * @return A ClusterQuery instance.\n\t */\n\tpublic FluentQuery offset(int value) {\n        mOffset = String.valueOf(value);\n        return this;\n\t}\n\n\t/**\n\t * Finds multiple records by the cluster parameters. You can use the below\n\t * way to finish a complicated query:\n\t *\n\t * <pre>\n\t * LitePal.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(1).offset(2)\n\t * \t\t.find(Person.class);\n\t * </pre>\n\t *\n\t * You can also do the same job with SQLiteDatabase like this:\n\t *\n\t * <pre>\n\t * getSQLiteDatabase().query(&quot;Person&quot;, &quot;name&quot;, &quot;age &gt; ?&quot;, new String[] { &quot;14&quot; }, null, null, &quot;age&quot;,\n\t * \t\t&quot;2,1&quot;);\n\t * </pre>\n\t *\n\t * Obviously, the first way is much more semantic.<br>\n\t * Note that the associated models won't be loaded by default considering\n\t * the efficiency, but you can do that by using\n\t * {@link FluentQuery#find(Class, boolean)}.\n\t *\n\t * @param modelClass\n\t *            Which table to query and the object type to return as a list.\n\t * @return An object list with founded data from database, or an empty list.\n\t */\n\tpublic <T> List<T> find(Class<T> modelClass) {\n        return find(modelClass, false);\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindMultiExecutor<T> findAsync(final Class<T> modelClass) {\n        return findAsync(modelClass, false);\n    }\n\n\t/**\n\t * It is mostly same as {@link FluentQuery#find(Class)} but an isEager\n\t * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n\t *\n\t * @param modelClass\n\t *            Which table to query and the object type to return as a list.\n\t * @param isEager\n\t *            True to load the associated models, false not.\n\t * @return An object list with founded data from database, or an empty list.\n\t */\n\tpublic <T> List<T> find(Class<T> modelClass, boolean isEager) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            String limit;\n            if (mOffset == null) {\n                limit = mLimit;\n            } else {\n                if (mLimit == null) {\n                    mLimit = \"0\";\n                }\n                limit = mOffset + \",\" + mLimit;\n            }\n            return queryHandler.onFind(modelClass, mColumns, mConditions, mOrderBy, limit, isEager);\n        }\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindMultiExecutor<T> findAsync(final Class<T> modelClass, final boolean isEager) {\n        final FindMultiExecutor<T> executor = new FindMultiExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final List<T> t = find(modelClass, isEager);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Finds the first record by the cluster parameters. You can use the below\n     * way to finish a complicated query:\n     *\n     * <pre>\n     * LitePal.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(10).offset(2)\n     * \t\t.findFirst(Person.class);\n     * </pre>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link FluentQuery#findFirst(Class, boolean)}.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @return An object with founded data from database, or null.\n     */\n    public <T> T findFirst(Class<T> modelClass) {\n        return findFirst(modelClass, false);\n    }\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> findFirstAsync(Class<T> modelClass) {\n        return findFirstAsync(modelClass, false);\n    }\n\n    /**\n     * It is mostly same as {@link FluentQuery#findFirst(Class)} but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return An object with founded data from database, or null.\n     */\n    public <T> T findFirst(Class<T> modelClass, boolean isEager) {\n        synchronized (LitePalSupport.class) {\n        \tString limitTemp = mLimit;\n        \tif (!\"0\".equals(mLimit)) { // If mLimit not equals to 0, set mLimit to 1 to find the first record.\n        \t\tmLimit = \"1\";\n\t\t\t}\n            List<T> list = find(modelClass, isEager);\n        \tmLimit = limitTemp; // Don't forget to change it back after finding operation.\n            if (list.size() > 0) {\n\t\t\t\tif (list.size() != 1) throw new LitePalSupportException(\"Found multiple records while only one record should be found at most.\");\n                return list.get(0);\n            }\n            return null;\n        }\n    }\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> findFirstAsync(final Class<T> modelClass, final boolean isEager) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = findFirst(modelClass, isEager);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Finds the last record by the cluster parameters. You can use the below\n     * way to finish a complicated query:\n     *\n     * <pre>\n     * LitePal.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(10).offset(2)\n     * \t\t.findLast(Person.class);\n     * </pre>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link FluentQuery#findLast(Class, boolean)}.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @return An object with founded data from database, or null.\n     */\n    public <T> T findLast(Class<T> modelClass) {\n        return findLast(modelClass, false);\n    }\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> findLastAsync(Class<T> modelClass) {\n        return findLastAsync(modelClass, false);\n    }\n\n    /**\n     * It is mostly same as {@link FluentQuery#findLast(Class)} but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return An object with founded data from database, or null.\n     */\n    public <T> T findLast(Class<T> modelClass, boolean isEager) {\n        synchronized (LitePalSupport.class) {\n\t\t\tString orderByTemp = mOrderBy;\n\t\t\tString limitTemp = mLimit;\n        \tif (TextUtils.isEmpty(mOffset) && TextUtils.isEmpty(mLimit)) { // If mOffset or mLimit is specified, we can't use the strategy in this block to speed up finding.\n\t\t\t\tif (TextUtils.isEmpty(mOrderBy)) {\n\t\t\t\t\t// If mOrderBy is null, we can use id desc order, then the first record will be the record value where want to find.\n\t\t\t\t\tmOrderBy = \"id desc\";\n\t\t\t\t} else {\n\t\t\t\t\t// If mOrderBy is not null, check if it ends with desc.\n\t\t\t\t\tif (mOrderBy.endsWith(\" desc\")) {\n\t\t\t\t\t\t// If mOrderBy ends with desc, then the last record of desc order will be the first record of asc order, so we remove the desc.\n\t\t\t\t\t\tmOrderBy = mOrderBy.replace(\" desc\", \"\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If mOrderBy not ends with desc, then the last record of asc order will be the first record of desc order, so we add the desc.\n\t\t\t\t\t\tmOrderBy += \" desc\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!\"0\".equals(mLimit)) {\n\t\t\t\t\tmLimit = \"1\";\n\t\t\t\t}\n\t\t\t}\n            List<T> list = find(modelClass, isEager);\n        \tmOrderBy = orderByTemp;\n        \tmLimit = limitTemp;\n            int size = list.size();\n            if (size > 0) {\n                return list.get(size - 1);\n            }\n            return null;\n        }\n    }\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> findLastAsync(final Class<T> modelClass, final boolean isEager) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = findLast(modelClass, isEager);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Count the records.\n\t *\n\t * <pre>\n\t * LitePal.count(Person.class);\n\t * </pre>\n\t *\n\t * This will count all rows in person table.<br>\n\t * You can also specify a where clause when counting.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(Person.class);\n\t * </pre>\n\t *\n\t * @param modelClass\n\t *            Which table to query from by class.\n\t * @return Count of the specified table.\n\t */\n\tpublic int count(Class<?> modelClass) {\n        return count(BaseUtility.changeCase(modelClass.getSimpleName()));\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public CountExecutor countAsync(Class<?> modelClass) {\n        return countAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())));\n    }\n\n\t/**\n\t * Count the records.\n\t *\n\t * <pre>\n\t * LitePal.count(&quot;person&quot;);\n\t * </pre>\n\t *\n\t * This will count all rows in person table.<br>\n\t * You can also specify a where clause when counting.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(&quot;person&quot;);\n\t * </pre>\n\t *\n\t * @param tableName\n\t *            Which table to query from.\n\t * @return Count of the specified table.\n\t */\n\tpublic int count(String tableName) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onCount(tableName, mConditions);\n        }\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public CountExecutor countAsync(final String tableName) {\n        final CountExecutor executor = new CountExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int count = count(tableName);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(count);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Calculates the average value on a given column.\n\t *\n\t * <pre>\n\t * LitePal.average(Person.class, &quot;age&quot;);\n\t * </pre>\n\t *\n\t * You can also specify a where clause when calculating.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(Person.class, &quot;age&quot;);\n\t * </pre>\n\t *\n\t * @param modelClass\n\t *            Which table to query from by class.\n\t * @param column\n\t *            The based on column to calculate.\n\t * @return The average value on a given column.\n\t */\n\tpublic double average(Class<?> modelClass, String column) {\n        return average(BaseUtility.changeCase(modelClass.getSimpleName()), column);\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public AverageExecutor averageAsync(final Class<?> modelClass, final String column) {\n        return averageAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), column);\n    }\n\n\t/**\n\t * Calculates the average value on a given column.\n\t *\n\t * <pre>\n\t * LitePal.average(&quot;person&quot;, &quot;age&quot;);\n\t * </pre>\n\t *\n\t * You can also specify a where clause when calculating.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(&quot;person&quot;, &quot;age&quot;);\n\t * </pre>\n\t *\n\t * @param tableName\n\t *            Which table to query from.\n\t * @param column\n\t *            The based on column to calculate.\n\t * @return The average value on a given column.\n\t */\n\tpublic double average(String tableName, String column) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onAverage(tableName, column, mConditions);\n        }\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public AverageExecutor averageAsync(final String tableName, final String column) {\n        final AverageExecutor executor = new AverageExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final double average = average(tableName, column);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(average);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Calculates the maximum value on a given column. The value is returned\n\t * with the same data type of the column.\n\t *\n\t * <pre>\n\t * LitePal.max(Person.class, &quot;age&quot;, int.class);\n\t * </pre>\n\t *\n\t * You can also specify a where clause when calculating.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(Person.class, &quot;age&quot;, Integer.TYPE);\n\t * </pre>\n\t *\n\t * @param modelClass\n\t *            Which table to query from by class.\n\t * @param columnName\n\t *            The based on column to calculate.\n\t * @param columnType\n\t *            The type of the based on column.\n\t * @return The maximum value on a given column.\n\t */\n\tpublic <T> T max(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return max(BaseUtility.changeCase(modelClass.getSimpleName()), columnName, columnType);\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> maxAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return maxAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n\t/**\n\t * Calculates the maximum value on a given column. The value is returned\n\t * with the same data type of the column.\n\t *\n\t * <pre>\n\t * LitePal.max(&quot;person&quot;, &quot;age&quot;, int.class);\n\t * </pre>\n\t *\n\t * You can also specify a where clause when calculating.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n\t * </pre>\n\t *\n\t * @param tableName\n\t *            Which table to query from.\n\t * @param columnName\n\t *            The based on column to calculate.\n\t * @param columnType\n\t *            The type of the based on column.\n\t * @return The maximum value on a given column.\n\t */\n\tpublic <T> T max(String tableName, String columnName, Class<T> columnType) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onMax(tableName, columnName, mConditions, columnType);\n        }\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> maxAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = max(tableName, columnName, columnType);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Calculates the minimum value on a given column. The value is returned\n\t * with the same data type of the column.\n\t *\n\t * <pre>\n\t * LitePal.min(Person.class, &quot;age&quot;, int.class);\n\t * </pre>\n\t *\n\t * You can also specify a where clause when calculating.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(Person.class, &quot;age&quot;, Integer.TYPE);\n\t * </pre>\n\t *\n\t * @param modelClass\n\t *            Which table to query from by class.\n\t * @param columnName\n\t *            The based on column to calculate.\n\t * @param columnType\n\t *            The type of the based on column.\n\t * @return The minimum value on a given column.\n\t */\n\tpublic <T> T min(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return min(BaseUtility.changeCase(modelClass.getSimpleName()), columnName, columnType);\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> minAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return minAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n\t/**\n\t * Calculates the minimum value on a given column. The value is returned\n\t * with the same data type of the column.\n\t *\n\t * <pre>\n\t * LitePal.min(&quot;person&quot;, &quot;age&quot;, int.class);\n\t * </pre>\n\t *\n\t * You can also specify a where clause when calculating.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n\t * </pre>\n\t *\n\t * @param tableName\n\t *            Which table to query from.\n\t * @param columnName\n\t *            The based on column to calculate.\n\t * @param columnType\n\t *            The type of the based on column.\n\t * @return The minimum value on a given column.\n\t */\n\tpublic <T> T min(String tableName, String columnName, Class<T> columnType) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onMin(tableName, columnName, mConditions, columnType);\n        }\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> minAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = min(tableName, columnName, columnType);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Calculates the sum of values on a given column. The value is returned\n\t * with the same data type of the column.\n\t *\n\t * <pre>\n\t * LitePal.sum(Person.class, &quot;age&quot;, int.class);\n\t * </pre>\n\t *\n\t * You can also specify a where clause when calculating.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(Person.class, &quot;age&quot;, Integer.TYPE);\n\t * </pre>\n\t *\n\t * @param modelClass\n\t *            Which table to query from by class.\n\t * @param columnName\n\t *            The based on column to calculate.\n\t * @param columnType\n\t *            The type of the based on column.\n\t * @return The sum value on a given column.\n\t */\n\tpublic <T> T sum(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return sum(BaseUtility.changeCase(modelClass.getSimpleName()), columnName, columnType);\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> sumAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return sumAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n    /**\n\t * Calculates the sum of values on a given column. The value is returned\n\t * with the same data type of the column.\n\t *\n\t * <pre>\n\t * LitePal.sum(&quot;person&quot;, &quot;age&quot;, int.class);\n\t * </pre>\n\t *\n\t * You can also specify a where clause when calculating.\n\t *\n\t * <pre>\n\t * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n\t * </pre>\n\t *\n\t * @param tableName\n\t *            Which table to query from.\n\t * @param columnName\n\t *            The based on column to calculate.\n\t * @param columnType\n\t *            The type of the based on column.\n\t * @return The sum value on a given column.\n\t */\n\tpublic <T> T sum(String tableName, String columnName, Class<T> columnType) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onSum(tableName, columnName, mConditions, columnType);\n        }\n\t}\n\n\t/**\n\t * This method is deprecated and will be removed in the future releases.\n\t * Handle async db operation in your own logic instead.\n\t */\n\t@Deprecated\n    public <T> FindExecutor<T> sumAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = sum(tableName, columnName, columnType);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/LitePal.kt",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal\n\nimport android.content.ContentValues\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport org.litepal.crud.LitePalSupport\nimport org.litepal.tablemanager.callback.DatabaseListener\n\n/**\n * LitePal is an Android library that allows developers to use SQLite database extremely easy.\n * You can initialized it by calling {@link #initialize(Context)} method to make LitePal ready to\n * work. Also you can switch the using database by calling {@link #use(LitePalDB)} and {@link #useDefault()}\n * methods.\n *\n * @author Tony Green\n * @since 2.0\n */\nobject LitePal {\n\n    /**\n     * Initialize to make LitePal ready to work. If you didn't configure LitePalApplication\n     * in the AndroidManifest.xml, make sure you call this method as soon as possible. In\n     * Application's onCreate() method will be fine.\n     *\n     * @param context\n     * Application context.\n     */\n    @JvmStatic\n    fun initialize(context: Context) {\n        Operator.initialize(context)\n    }\n\n    /**\n     * Get a writable SQLiteDatabase.\n     *\n     * @return A writable SQLiteDatabase instance\n     */\n    @JvmStatic\n    fun getDatabase(): SQLiteDatabase = Operator.getDatabase()\n\n    /**\n     * Begins a transaction in EXCLUSIVE mode.\n     */\n    @JvmStatic\n    fun beginTransaction() = Operator.beginTransaction()\n\n    /**\n     * End a transaction.\n     */\n    @JvmStatic\n    fun endTransaction() = Operator.endTransaction()\n\n    /**\n     * Marks the current transaction as successful. Do not do any more database work between calling this and calling endTransaction.\n     * Do as little non-database work as possible in that situation too.\n     * If any errors are encountered between this and endTransaction the transaction will still be committed.\n     */\n    @JvmStatic\n    fun setTransactionSuccessful() = Operator.setTransactionSuccessful()\n\n    /**\n     * Switch the using database to the one specified by parameter.\n     * @param litePalDB\n     * The database to switch to.\n     */\n    @JvmStatic\n    fun use(litePalDB: LitePalDB) {\n        Operator.use(litePalDB)\n    }\n\n\n    /**\n     * Switch the using database to default with configuration by litepal.xml.\n     */\n    @JvmStatic\n    fun useDefault() {\n        Operator.useDefault()\n    }\n\n    /**\n     * Delete the specified database.\n     * @param dbName\n     * Name of database to delete.\n     * @return True if delete success, false otherwise.\n     */\n    @JvmStatic\n    fun deleteDatabase(dbName: String) = Operator.deleteDatabase(dbName)\n\n    @JvmStatic\n    fun aesKey(key: String) {\n        Operator.aesKey(key)\n    }\n\n    /**\n     * Declaring to query which columns in table.\n     *\n     * LitePal.select(&quot;name&quot;, &quot;age&quot;).find(Person.class);\n     *\n     * This will find all rows with name and age columns in Person table.\n     *\n     * @param columns\n     * A String array of which columns to return. Passing null will\n     * return all columns.\n     *\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun select(vararg columns: String?) = Operator.select(*columns)\n\n    /**\n     * Declaring to query which rows in table.\n     *\n     * LitePal.where(&quot;name = ? or age &gt; ?&quot;, &quot;Tom&quot;, &quot;14&quot;).find(Person.class);\n     *\n     * This will find rows which name is Tom or age greater than 14 in Person\n     * table.\n     *\n     * @param conditions\n     * A filter declaring which rows to return, formatted as an SQL\n     * WHERE clause. Passing null will return all rows.\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun where(vararg conditions: String?) = Operator.where(*conditions)\n\n    /**\n     * Declaring how to order the rows queried from table.\n     *\n     * LitePal.order(&quot;name desc&quot;).find(Person.class);\n     *\n     * This will find all rows in Person table sorted by name with inverted\n     * order.\n     *\n     * @param column\n     * How to order the rows, formatted as an SQL ORDER BY clause.\n     * Passing null will use the default sort order, which may be\n     * unordered.\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun order(column: String?) = Operator.order(column)\n\n    /**\n     * Limits the number of rows returned by the query.\n     *\n     * LitePal.limit(2).find(Person.class);\n     *\n     * This will find the top 2 rows in Person table.\n     *\n     * @param value\n     * Limits the number of rows returned by the query, formatted as\n     * LIMIT clause.\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun limit(value: Int) = Operator.limit(value)\n\n    /**\n     * Declaring the offset of rows returned by the query. This method must be\n     * used with [LitePal.limit], or nothing will return.\n     *\n     * LitePal.limit(1).offset(2).find(Person.class);\n     *\n     * This will find the third row in Person table.\n     *\n     * @param value\n     * The offset amount of rows returned by the query.\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun offset(value: Int) = Operator.offset(value)\n\n    /**\n     * Count the records.\n     *\n     * LitePal.count(Person.class);\n     *\n     * This will count all rows in person table.\n     *\n     * You can also specify a where clause when counting.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(Person.class);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @return Count of the specified table.\n     */\n    @JvmStatic\n    fun count(modelClass: Class<*>) = Operator.count(modelClass)\n\n    /**\n     * Basically same as [LitePal.count] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @return A CountExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun countAsync(modelClass: Class<*>) = Operator.countAsync(modelClass)\n\n    /**\n     * Count the records.\n     *\n     * LitePal.count(&quot;person&quot;);\n     *\n     * This will count all rows in person table.\n     *\n     * You can also specify a where clause when counting.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(&quot;person&quot;);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @return Count of the specified table.\n     */\n    @JvmStatic\n    fun count(tableName: String) = Operator.count(tableName)\n\n    /**\n     * Basically same as [LitePal.count] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @return A CountExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun countAsync(tableName: String) = Operator.countAsync(tableName)\n\n    /**\n     * Calculates the average value on a given column.\n     *\n     * LitePal.average(Person.class, &quot;age&quot;);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(Person.class, &quot;age&quot;);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param column\n     * The based on column to calculate.\n     * @return The average value on a given column.\n     */\n    @JvmStatic\n    fun average(modelClass: Class<*>, column: String) = Operator.average(modelClass, column)\n\n    /**\n     * Basically same as [LitePal.average] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param column\n     * The based on column to calculate.\n     * @return A AverageExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun averageAsync(modelClass: Class<*>, column: String) = Operator.averageAsync(modelClass, column)\n\n    /**\n     * Calculates the average value on a given column.\n     *\n     * LitePal.average(&quot;person&quot;, &quot;age&quot;);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(&quot;person&quot;, &quot;age&quot;);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param column\n     * The based on column to calculate.\n     * @return The average value on a given column.\n     */\n    @JvmStatic\n    fun average(tableName: String, column: String) = Operator.average(tableName, column)\n\n    /**\n     * Basically same as [LitePal.average] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param column\n     * The based on column to calculate.\n     * @return A AverageExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun averageAsync(tableName: String, column: String) = Operator.averageAsync(tableName, column)\n\n    /**\n     * Calculates the maximum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.max(Person.class, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(Person.class, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The maximum value on a given column.\n     */\n    @JvmStatic\n    fun <T> max(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.max(modelClass, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.max] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> maxAsync(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.maxAsync(modelClass, columnName, columnType)\n\n    /**\n     * Calculates the maximum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.max(&quot;person&quot;, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The maximum value on a given column.\n     */\n    @JvmStatic\n    fun <T> max(tableName: String, columnName: String, columnType: Class<T>) = Operator.max(tableName, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.max] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> maxAsync(tableName: String, columnName: String, columnType: Class<T>) = Operator.maxAsync(tableName, columnName, columnType)\n\n    /**\n     * Calculates the minimum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.min(Person.class, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(Person.class, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The minimum value on a given column.\n     */\n    @JvmStatic\n    fun <T> min(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.min(modelClass, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.min] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> minAsync(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.minAsync(modelClass, columnName, columnType)\n\n    /**\n     * Calculates the minimum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.min(&quot;person&quot;, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The minimum value on a given column.\n     */\n    @JvmStatic\n    fun <T> min(tableName: String, columnName: String, columnType: Class<T>) = Operator.min(tableName, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.min] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> minAsync(tableName: String, columnName: String, columnType: Class<T>) = Operator.minAsync(tableName, columnName, columnType)\n\n    /**\n     * Calculates the sum of values on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.sum(Person.class, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(Person.class, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The sum value on a given column.\n     */\n    @JvmStatic\n    fun <T> sum(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.sum(modelClass, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.sum] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> sumAsync(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.sumAsync(modelClass, columnName, columnType)\n\n    /**\n     * Calculates the sum of values on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.sum(&quot;person&quot;, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The sum value on a given column.\n     */\n    @JvmStatic\n    fun <T> sum(tableName: String, columnName: String, columnType: Class<T>) = Operator.sum(tableName, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.sum] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> sumAsync(tableName: String, columnName: String, columnType: Class<T>) = Operator.sumAsync(tableName, columnName, columnType)\n\n    /**\n     * Finds the record by a specific id.\n     *\n     * Person p = LitePal.find(Person.class, 1);\n     *\n     * The modelClass determines which table to query and the object type to\n     * return. If no record can be found, then return null.\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * [LitePal.find].\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param id\n     * Which record to query.\n     * @return An object with found data from database, or null.\n     */\n    @JvmStatic\n    fun <T> find(modelClass: Class<T>, id: Long) = Operator.find(modelClass, id)\n\n    /**\n     * Basically same as [LitePal.find] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param id\n     * Which record to query.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> findAsync(modelClass: Class<T>, id: Long) = Operator.findAsync(modelClass, id)\n\n    /**\n     * It is mostly same as [LitePal.find] but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     *\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param id\n     * Which record to query.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return An object with found data from database, or null.\n     */\n    @JvmStatic\n    fun <T> find(modelClass: Class<T>, id: Long, isEager: Boolean) = Operator.find(modelClass, id, isEager)\n\n    /**\n     * Basically same as [LitePal.find] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param id\n     * Which record to query.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> findAsync(modelClass: Class<T>, id: Long, isEager: Boolean) = Operator.findAsync(modelClass, id, isEager)\n\n    /**\n     * Finds the first record of a single table.\n     *\n     * Person p = LitePal.findFirst(Person.class);\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * [LitePal.findFirst].\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @return An object with data of first row, or null.\n     */\n    @JvmStatic\n    fun <T> findFirst(modelClass: Class<T>) = Operator.findFirst(modelClass)\n\n    /**\n     * Basically same as [LitePal.findFirst] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> findFirstAsync(modelClass: Class<T>) = Operator.findFirstAsync(modelClass)\n\n    /**\n     * It is mostly same as [LitePal.findFirst] but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     *\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return An object with data of first row, or null.\n     */\n    @JvmStatic\n    fun <T> findFirst(modelClass: Class<T>, isEager: Boolean) = Operator.findFirst(modelClass, isEager)\n\n    /**\n     * Basically same as [LitePal.findFirst] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> findFirstAsync(modelClass: Class<T>, isEager: Boolean) = Operator.findFirstAsync(modelClass, isEager)\n\n    /**\n     * Finds the last record of a single table.\n     *\n     * Person p = LitePal.findLast(Person.class);\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * [LitePal.findLast].\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @return An object with data of last row, or null.\n     */\n    @JvmStatic\n    fun <T> findLast(modelClass: Class<T>) = Operator.findLast(modelClass)\n\n    /**\n     * Basically same as [LitePal.findLast] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> findLastAsync(modelClass: Class<T>) = Operator.findLastAsync(modelClass)\n\n    /**\n     * It is mostly same as [LitePal.findLast] but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     *\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return An object with data of last row, or null.\n     */\n    @JvmStatic\n    fun <T> findLast(modelClass: Class<T>, isEager: Boolean) = Operator.findLast(modelClass, isEager)\n\n    /**\n     * Basically same as [LitePal.findLast] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> findLastAsync(modelClass: Class<T>, isEager: Boolean) = Operator.findLastAsync(modelClass, isEager)\n\n    /**\n     * Finds multiple records by an id array.\n     *\n     * List&lt;Person&gt; people = LitePal.findAll(Person.class, 1, 2, 3);\n     *\n     * long[] bookIds = { 10, 18 };\n     * List&lt;Book&gt; books = LitePal.findAll(Book.class, bookIds);\n     *\n     * Of course you can find all records by passing nothing to the ids\n     * parameter.\n     *\n     * List&lt;Book&gt; allBooks = LitePal.findAll(Book.class);\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * [LitePal.findAll].\n     *\n     * The modelClass determines which table to query and the object type to\n     * return.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return as a list.\n     * @param ids\n     * Which records to query. Or do not pass it to find all records.\n     * @return An object list with found data from database, or an empty list.\n     */\n    @JvmStatic\n    fun <T> findAll(modelClass: Class<T>, vararg ids: Long) = Operator.findAll(modelClass, *ids)\n\n    /**\n     * Basically same as [LitePal.findAll] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return as a list.\n     * @param ids\n     * Which records to query. Or do not pass it to find all records.\n     * @return A FindMultiExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> findAllAsync(modelClass: Class<T>, vararg ids: Long) = Operator.findAllAsync(modelClass, *ids)\n\n    /**\n     * It is mostly same as [LitePal.findAll] but an\n     * isEager parameter. If set true the associated models will be loaded as well.\n     *\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return as a list.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @param ids\n     * Which records to query. Or do not pass it to find all records.\n     * @return An object list with found data from database, or an empty list.\n     */\n    @JvmStatic\n    fun <T> findAll(modelClass: Class<T>, isEager: Boolean, vararg ids: Long) = Operator.findAll(modelClass, isEager, *ids)\n\n    /**\n     * Basically same as [LitePal.findAll] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return as a list.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @param ids\n     * Which records to query. Or do not pass it to find all records.\n     * @return A FindMultiExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T> findAllAsync(modelClass: Class<T>, isEager: Boolean, vararg ids: Long) = Operator.findAllAsync(modelClass, isEager, *ids)\n\n    /**\n     * Runs the provided SQL and returns a Cursor over the result set. You may\n     * include ? in where clause in the query, which will be replaced by the\n     * second to the last parameters, such as:\n     *\n     * Cursor cursor = LitePal.findBySQL(&quot;select * from person where name=? and age=?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     *\n     * @param sql\n     * First parameter is the SQL clause to apply. Second to the last\n     * parameters will replace the place holders.\n     * @return A Cursor object, which is positioned before the first entry. Note\n     * that Cursors are not synchronized, see the documentation for more\n     * details.\n     */\n    @JvmStatic\n    fun findBySQL(vararg sql: String) = Operator.findBySQL(*sql)\n\n    /**\n     * Deletes the record in the database by id.\n     *\n     * The data in other tables which is referenced with the record will be\n     * removed too.\n     *\n     * LitePal.delete(Person.class, 1);\n     *\n     * This means that the record 1 in person table will be removed.\n     *\n     * @param modelClass\n     * Which table to delete from by class.\n     * @param id\n     * Which record to delete.\n     * @return The number of rows affected. Including cascade delete rows.\n     */\n    @JvmStatic\n    fun delete(modelClass: Class<*>, id: Long) = Operator.delete(modelClass, id)\n\n    /**\n     * Basically same as [LitePal.delete] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to delete from by class.\n     * @param id\n     * Which record to delete.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun deleteAsync(modelClass: Class<*>, id: Long) = Operator.deleteAsync(modelClass, id)\n\n    /**\n     * Deletes all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL DELETE statement and sends\n     * it to the database.\n     *\n     * LitePal.deleteAll(Person.class, &quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     *\n     * This means that all the records which name is Tom and age is 14 will be\n     * removed.\n     *\n     * @param modelClass\n     * Which table to delete from by class.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * deleting. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will delete\n     * all rows.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun deleteAll(modelClass: Class<*>, vararg conditions: String?) = Operator.deleteAll(modelClass, *conditions)\n\n    /**\n     * Basically same as [LitePal.deleteAll] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to delete from by class.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * deleting. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will delete\n     * all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun deleteAllAsync(modelClass: Class<*>, vararg conditions: String?) = Operator.deleteAllAsync(modelClass, *conditions)\n\n    /**\n     * Deletes all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL DELETE statement and sends\n     * it to the database.\n     *\n     * LitePal.deleteAll(&quot;person&quot;, &quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     *\n     * This means that all the records which name is Tom and age is 14 will be\n     * removed.\n     *\n     * Note that this method won't delete the referenced data in other tables.\n     * You should remove those values by your own.\n     *\n     * @param tableName\n     * Which table to delete from.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * deleting. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will delete\n     * all rows.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun deleteAll(tableName: String, vararg conditions: String?) = Operator.deleteAll(tableName, *conditions)\n\n    /**\n     * Basically same as [LitePal.deleteAll] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to delete from.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * deleting. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will delete\n     * all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun deleteAllAsync(tableName: String, vararg conditions: String?) = Operator.deleteAllAsync(tableName, *conditions)\n\n    /**\n     * Updates the corresponding record by id with ContentValues. Returns the\n     * number of affected rows.\n     *\n     * ContentValues cv = new ContentValues();\n     *\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     *\n     * LitePal.update(Person.class, cv, 1);\n     *\n     * This means that the name of record 1 will be updated into Jim.\n     *\n     * @param modelClass\n     * Which table to update by class.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param id\n     * Which record to update.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun update(modelClass: Class<*>, values: ContentValues, id: Long) = Operator.update(modelClass, values, id)\n\n    /**\n     * Basically same as [LitePal.update] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to update by class.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param id\n     * Which record to update.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun updateAsync(modelClass: Class<*>, values: ContentValues, id: Long) = Operator.updateAsync(modelClass, values, id)\n\n    /**\n     * Updates all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL UPDATE statement and sends\n     * it to the database.\n     *\n     * ContentValues cv = new ContentValues();\n     *\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     *\n     * LitePal.update(Person.class, cv, &quot;name = ?&quot;, &quot;Tom&quot;);\n     *\n     * This means that all the records which name is Tom will be updated into\n     * Jim.\n     *\n     * @param modelClass\n     * Which table to update by class.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * updating. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will update\n     * all rows.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun updateAll(modelClass: Class<*>, values: ContentValues, vararg conditions: String?) = Operator.updateAll(modelClass, values, *conditions)\n\n    /**\n     * Basically same as [LitePal.updateAll] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to update by class.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * updating. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will update\n     * all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun updateAllAsync(modelClass: Class<*>, values: ContentValues, vararg conditions: String?) = Operator.updateAllAsync(modelClass, values, *conditions)\n\n    /**\n     * Updates all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL UPDATE statement and sends\n     * it to the database.\n     *\n     * ContentValues cv = new ContentValues();\n     *\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     *\n     * LitePal.update(&quot;person&quot;, cv, &quot;name = ?&quot;, &quot;Tom&quot;);\n     *\n     * This means that all the records which name is Tom will be updated into\n     * Jim.\n     *\n     * @param tableName\n     * Which table to update.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * updating. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will update\n     * all rows.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun updateAll(tableName: String, values: ContentValues, vararg conditions: String?) = Operator.updateAll(tableName, values, *conditions)\n\n    /**\n     * Basically same as [LitePal.updateAll] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to update.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * updating. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will update\n     * all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun updateAllAsync(tableName: String, values: ContentValues, vararg conditions: String?) = Operator.updateAllAsync(tableName, values, *conditions)\n\n    /**\n     * Saves the collection into database.\n     *\n     * LitePal.saveAll(people);\n     *\n     * If the model in collection is a new record gets created in the database,\n     * otherwise the existing record gets updated.\n     *\n     * If saving process failed by any accident, the whole action will be\n     * cancelled and your database will be **rolled back**.\n     *\n     * This method acts the same result as the below way, but **much more\n     * efficient**.\n     *\n     * for (Person person : people) {\n     *      person.save();\n     * }\n     *\n     * So when your collection holds huge of models, saveAll(Collection) is the better choice.\n     *\n     * @param collection\n     * Holds all models to save.\n     * @return True if all records in collection are saved. False none record in collection is saved. There won't be partial saved condition.\n     */\n    @JvmStatic\n    fun <T : LitePalSupport> saveAll(collection: Collection<T>) = Operator.saveAll(collection)\n\n    /**\n     * Basically same as [LitePal.saveAll] but pending to a new thread for executing.\n     *\n     * @param collection\n     * Holds all models to save.\n     * @return A SaveExecutor instance.\n     */\n    @JvmStatic\n    @Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\n    fun <T : LitePalSupport> saveAllAsync(collection: Collection<T>) = Operator.saveAllAsync(collection)\n\n    /**\n     * Provide a way to mark all models in collection as deleted. This means these models' save\n     * state is no longer exist anymore. If save them again, they will be treated as inserting new\n     * data instead of updating the exist one.\n     * @param collection\n     * Collection of models which want to mark as deleted and clear their save state.\n     */\n    @JvmStatic\n    fun <T : LitePalSupport> markAsDeleted(collection: Collection<T>) {\n        Operator.markAsDeleted(collection)\n    }\n\n    /**\n     * Check if the specified conditions data already exists in the table.\n     * @param modelClass\n     * Which table to check by class.\n     * @param conditions\n     * A filter declaring which data to check. Exactly same use as\n     * [LitePal.where], except null conditions will result in false.\n     * @return Return true if the specified conditions data already exists in the table.\n     * False otherwise. Null conditions will result in false.\n     */\n    @JvmStatic\n    fun <T> isExist(modelClass: Class<T>, vararg conditions: String?) = Operator.isExist(modelClass, *conditions)\n\n    /**\n     * Register a listener to listen database create and upgrade events.\n     */\n    @JvmStatic\n    fun registerDatabaseListener(listener: DatabaseListener) {\n        Operator.registerDatabaseListener(listener)\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/LitePalApplication.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal;\n\nimport org.litepal.exceptions.GlobalException;\n\nimport android.annotation.SuppressLint;\nimport android.app.Application;\nimport android.content.Context;\nimport android.os.Handler;\nimport android.os.Looper;\n\n/**\n * Base class of LitePal to make things easier when developers need to use\n * context. When you need context, just use\n * <b>LitePalApplication.getContext()</b>. To make this function work, you need\n * to configure your AndroidManifest.xml. Specifying\n * <b>\"org.litepal.LitePalApplication\"</b> as the application name in your\n * &lt;application&gt; tag to enable LitePal get the context. Of course if you\n * need to write your own Application class, LitePal can still live with that.\n * But just remember make your own Application class inherited from\n * LitePalApplication instead of inheriting from Application directly. This can\n * make all things work without side effects. <br>\n * Besides if you don't want use the above way, you can also call the LitePal.initialize(Context)\n * method to do the same job. Just remember call this method as early as possible, in Application's onCreate()\n * method will be fine.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class LitePalApplication extends Application {\n\n\t/**\n\t * Global application context.\n\t */\n\t@SuppressLint(\"StaticFieldLeak\")\n\tpublic static Context sContext;\n\n\tpublic static Handler sHandler = new Handler(Looper.getMainLooper());\n\n\t/**\n\t * Construct of LitePalApplication. Initialize application context.\n\t */\n\tpublic LitePalApplication() {\n\t\tsContext = this;\n\t}\n\n\t/**\n\t * Get the global application context.\n\t * \n\t * @return Application context.\n\t * @throws org.litepal.exceptions.GlobalException\n\t */\n\tpublic static Context getContext() {\n\t\tif (sContext == null) {\n\t\t\tthrow new GlobalException(GlobalException.APPLICATION_CONTEXT_IS_NULL);\n\t\t}\n\t\treturn sContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/LitePalBase.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal;\n\nimport org.litepal.annotation.Column;\nimport org.litepal.crud.LitePalSupport;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.exceptions.DatabaseGenerateException;\nimport org.litepal.parser.LitePalAttr;\nimport org.litepal.tablemanager.model.AssociationsModel;\nimport org.litepal.tablemanager.model.ColumnModel;\nimport org.litepal.tablemanager.model.GenericModel;\nimport org.litepal.tablemanager.model.TableModel;\nimport org.litepal.tablemanager.typechange.BlobOrm;\nimport org.litepal.tablemanager.typechange.BooleanOrm;\nimport org.litepal.tablemanager.typechange.DateOrm;\nimport org.litepal.tablemanager.typechange.DecimalOrm;\nimport org.litepal.tablemanager.typechange.NumericOrm;\nimport org.litepal.tablemanager.typechange.OrmChange;\nimport org.litepal.tablemanager.typechange.TextOrm;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.Const;\nimport org.litepal.util.DBUtility;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Base class of all the LitePal components. If each component need to\n * interactive with other components or they have some same logic with duplicate\n * codes, LitePalBase may be the solution.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic abstract class LitePalBase {\n\n\tpublic static final String TAG = \"LitePalBase\";\n\n\t/**\n\t * Action to get associations.\n\t */\n\tprivate static final int GET_ASSOCIATIONS_ACTION = 1;\n\n\t/**\n\t * Action to get association info.\n\t */\n\tprivate static final int GET_ASSOCIATION_INFO_ACTION = 2;\n\n\t/**\n\t * All the supporting mapping types currently in the array.\n\t */\n\tprivate OrmChange[] typeChangeRules = { new NumericOrm(), new TextOrm(), new BooleanOrm(),\n\t\t\tnew DecimalOrm(), new DateOrm(), new BlobOrm()};\n\n    /**\n     * This is map of class name to fields list. Indicates that each class has which supported fields.\n     */\n    private Map<String, List<Field>> classFieldsMap = new HashMap<>();\n\n\t/**\n\t * This is map of class name to generic fields list. Indicates that each class has which supported generic fields.\n\t */\n    private Map<String, List<Field>> classGenericFieldsMap = new HashMap<>();\n\n\t/**\n\t * The collection contains all association models.\n\t */\n\tprivate Collection<AssociationsModel> mAssociationModels;\n\n\t/**\n\t * The collection contains all association info.\n\t */\n\tprivate Collection<AssociationsInfo> mAssociationInfos;\n\n    /**\n     * The collection contains all generic models.\n     */\n    private Collection<GenericModel> mGenericModels;\n\n\t/**\n\t * This method is used to get the table model by the class name passed\n\t * in. The principle to generate table model is that each field in the class\n\t * with non-static modifier and has a type among int/Integer, long/Long,\n\t * short/Short, float/Float, double/Double, char/Character, boolean/Boolean\n\t * or String, would generate a column with same name as corresponding field.\n\t * If users don't want some of the fields map a column, declare an ignore\n     * annotation with {@link Column#ignore()}.\n\t * \n\t * @param className\n\t *            The full name of the class to map in database.\n\t * @return A table model with table name, class name and the map of column\n\t *         name and column type.\n\t */\n\tprotected TableModel getTableModel(String className) {\n\t\tString tableName = DBUtility.getTableNameByClassName(className);\n\t\tTableModel tableModel = new TableModel();\n\t\ttableModel.setTableName(tableName);\n\t\ttableModel.setClassName(className);\n\t\tList<Field> supportedFields = getSupportedFields(className);\n\t\tfor (Field field : supportedFields) {\n            ColumnModel columnModel = convertFieldToColumnModel(field);\n            tableModel.addColumnModel(columnModel);\n\t\t}\n\t\treturn tableModel;\n\t}\n\n\t/**\n\t * This method is used to get association models depends on the given class\n\t * name list.\n\t * \n\t * @param classNames\n\t *            The names of the classes that want to get their associations.\n\t * @return Collection of association models.\n\t */\n\tprotected Collection<AssociationsModel> getAssociations(List<String> classNames) {\n\t\tif (mAssociationModels == null) {\n\t\t\tmAssociationModels = new HashSet<>();\n\t\t}\n        if (mGenericModels == null) {\n            mGenericModels = new HashSet<>();\n        }\n\t\tmAssociationModels.clear();\n        mGenericModels.clear();\n\t\tfor (String className : classNames) {\n\t\t\tanalyzeClassFields(className, GET_ASSOCIATIONS_ACTION);\n\t\t}\n\t\treturn mAssociationModels;\n\t}\n\n    /**\n     * Get all generic models for create generic tables.\n     * @return All generic models.\n     */\n    protected Collection<GenericModel> getGenericModels() {\n        return mGenericModels;\n    }\n\n\t/**\n\t * Get the association info model by the class name.\n\t * \n\t * @param className\n\t *            The class name to introspection.\n\t * @return Collection of association info.\n\t */\n\tprotected Collection<AssociationsInfo> getAssociationInfo(String className) {\n\t\tif (mAssociationInfos == null) {\n\t\t\tmAssociationInfos = new HashSet<>();\n\t\t}\n\t\tmAssociationInfos.clear();\n\t\tanalyzeClassFields(className, GET_ASSOCIATION_INFO_ACTION);\n\t\treturn mAssociationInfos;\n\t}\n\n\t/**\n\t * Find all the fields in the class. But not each field is supported to add\n\t * a column to the table. Only the basic data types and String are\n\t * supported. This method will intercept all the types which are not\n\t * supported and return a new list of supported fields.\n\t * \n\t * @param className\n\t *            The full name of the class.\n\t * @return A list of supported fields.\n\t */\n\tprotected List<Field> getSupportedFields(String className) {\n        List<Field> fieldList = classFieldsMap.get(className);\n        if (fieldList == null) {\n            List<Field> supportedFields = new ArrayList<>();\n            Class<?> clazz;\n            try {\n                clazz = Class.forName(className);\n            } catch (ClassNotFoundException e) {\n                throw new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);\n            }\n            recursiveSupportedFields(clazz, supportedFields);\n            classFieldsMap.put(className, supportedFields);\n            return supportedFields;\n        }\n        return fieldList;\n\t}\n\n    /**\n     * Find all supported generic fields in the class. Supporting rule is in {@link BaseUtility#isGenericTypeSupported(String)}.\n     * @param className\n     *           The full name of the class.\n     * @return A list of supported generic fields.\n     */\n\tprotected List<Field> getSupportedGenericFields(String className) {\n        List<Field> genericFieldList = classGenericFieldsMap.get(className);\n        if (genericFieldList == null) {\n            List<Field> supportedGenericFields = new ArrayList<>();\n            Class<?> clazz;\n            try {\n                clazz = Class.forName(className);\n            } catch (ClassNotFoundException e) {\n                throw new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);\n            }\n            recursiveSupportedGenericFields(clazz, supportedGenericFields);\n            classGenericFieldsMap.put(className, supportedGenericFields);\n            return supportedGenericFields;\n        }\n        return genericFieldList;\n\t}\n\n\t/**\n\t * If the field type implements from List or Set, regard it as a collection.\n\t * \n\t * @param fieldType\n\t *            The field type.\n\t * @return True if the field type is collection, false otherwise.\n\t */\n\tprotected boolean isCollection(Class<?> fieldType) {\n\t\treturn isList(fieldType) || isSet(fieldType);\n\t}\n\n\t/**\n\t * If the field type implements from List, regard it as a list.\n\t * \n\t * @param fieldType\n\t *            The field type.\n\t * @return True if the field type is List, false otherwise.\n\t */\n\tprotected boolean isList(Class<?> fieldType) {\n\t\treturn List.class.isAssignableFrom(fieldType);\n\t}\n\n\t/**\n\t * If the field type implements from Set, regard it as a set.\n\t * \n\t * @param fieldType\n\t *            The field type.\n\t * @return True if the field type is Set, false otherwise.\n\t */\n\tprotected boolean isSet(Class<?> fieldType) {\n\t\treturn Set.class.isAssignableFrom(fieldType);\n\t}\n\n\t/**\n\t * Judge the passed in column is an id column or not. The column named id or\n\t * _id will be considered as id column.\n\t * \n\t * @param columnName\n\t *            The name of column.\n\t * @return Return true if it's id column, otherwise return false.\n\t */\n\tprotected boolean isIdColumn(String columnName) {\n\t\treturn \"_id\".equalsIgnoreCase(columnName) || \"id\".equalsIgnoreCase(columnName);\n\t}\n\n\t/**\n\t * If two tables are associated, one table have a foreign key column. The\n\t * foreign key column name will be the associated table name with _id\n\t * appended.\n\t * \n\t * @param associatedTableName\n\t *            The associated table name.\n\t * @return The foreign key column name.\n\t */\n\tprotected String getForeignKeyColumnName(String associatedTableName) {\n\t\treturn BaseUtility.changeCase(associatedTableName + \"_id\");\n\t}\n\n    /**\n     * Get the column type for creating table by field type.\n     * @param fieldType\n     *          Type of field.\n     * @return The column type for creating table.\n     */\n    protected String getColumnType(String fieldType) {\n        String columnType;\n        for (OrmChange ormChange : typeChangeRules) {\n            columnType = ormChange.object2Relation(fieldType);\n            if (columnType != null) {\n                return columnType;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Get the generic type class of List or Set. If there's no generic type of\n     * List or Set return null.\n     *\n     * @param field\n     *            A generic type field.\n     * @return The generic type of List or Set.\n     */\n    protected Class<?> getGenericTypeClass(Field field) {\n        Type genericType = field.getGenericType();\n        if (genericType != null) {\n            if (genericType instanceof ParameterizedType) {\n                ParameterizedType parameterizedType = (ParameterizedType) genericType;\n                return (Class<?>) parameterizedType.getActualTypeArguments()[0];\n            }\n        }\n        return null;\n    }\n\n    private void recursiveSupportedFields(Class<?> clazz, List<Field> supportedFields) {\n        if (clazz == LitePalSupport.class || clazz == Object.class) {\n            return;\n        }\n        Field[] fields = clazz.getDeclaredFields();\n        if (fields.length > 0) {\n            for (Field field : fields) {\n                Column annotation = field.getAnnotation(Column.class);\n                if (annotation != null && annotation.ignore()) {\n                    continue;\n                }\n                int modifiers = field.getModifiers();\n                if (!Modifier.isStatic(modifiers)) {\n                    Class<?> fieldTypeClass = field.getType();\n                    String fieldType = fieldTypeClass.getName();\n                    if (BaseUtility.isFieldTypeSupported(fieldType)) {\n                        supportedFields.add(field);\n                    }\n                }\n            }\n        }\n        recursiveSupportedFields(clazz.getSuperclass(), supportedFields);\n    }\n\n    private void recursiveSupportedGenericFields(Class<?> clazz, List<Field> supportedGenericFields) {\n        if (clazz == LitePalSupport.class || clazz == Object.class) {\n            return;\n        }\n        Field[] fields = clazz.getDeclaredFields();\n        if (fields.length > 0) {\n            for (Field field : fields) {\n                Column annotation = field.getAnnotation(Column.class);\n                if (annotation != null && annotation.ignore()) {\n                    continue;\n                }\n                int modifiers = field.getModifiers();\n                if (!Modifier.isStatic(modifiers) && isCollection(field.getType())) {\n                    String genericTypeName = getGenericTypeName(field);\n                    if (BaseUtility.isGenericTypeSupported(genericTypeName) || clazz.getName().equalsIgnoreCase(genericTypeName)) {\n                        supportedGenericFields.add(field);\n                    }\n                }\n            }\n        }\n        recursiveSupportedGenericFields(clazz.getSuperclass(), supportedGenericFields);\n    }\n\n\t/**\n\t * Introspection of the passed in class. Analyze the fields of current class\n\t * and find out the associations of it.\n\t * \n\t * @param className\n\t *            The class name to introspection.\n\t * @param action\n\t *            Between {@link org.litepal.LitePalBase#GET_ASSOCIATIONS_ACTION} and\n\t *            {@link org.litepal.LitePalBase#GET_ASSOCIATION_INFO_ACTION}\n\t */\n\tprivate void analyzeClassFields(String className, int action) {\n\t\ttry {\n            Class<?> dynamicClass = Class.forName(className);\n\t\t\tField[] fields = dynamicClass.getDeclaredFields();\n\t\t\tfor (Field field : fields) {\n\t\t\t\tif (isNonPrimitive(field)) {\n                    Column annotation = field.getAnnotation(Column.class);\n                    if (annotation != null && annotation.ignore()) {\n                        continue;\n                    }\n\t\t\t\t\toneToAnyConditions(className, field, action);\n\t\t\t\t\tmanyToAnyConditions(className, field, action);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (ClassNotFoundException ex) {\n\t\t\tex.printStackTrace();\n\t\t\tthrow new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);\n\t\t}\n\t}\n\n\t/**\n\t * Check the field is a non primitive field or not.\n\t * \n\t * @param field\n\t *            The field to check.\n\t * @return True if the field is non primitive, false otherwise.\n\t */\n\tprivate boolean isNonPrimitive(Field field) {\n\t\treturn !field.getType().isPrimitive();\n\t}\n\n    /**\n     * Check the field is a private field or not.\n     *\n     * @param field\n     *            The field to check.\n     * @return True if the field is private, false otherwise.\n     */\n\tprotected boolean isPrivate(Field field) {\n        return Modifier.isPrivate(field.getModifiers());\n    }\n\n\t/**\n\t * Deals with one to any association conditions. e.g. Song and Album. An\n\t * album have many songs, and a song belongs to one album. So if there's an\n\t * Album model defined in Song with private modifier, and in Album there's a\n\t * List or Set with generic type of Song and declared as private modifier,\n\t * they are one2many association. If there's no List or Set defined in\n\t * Album, they will become one2one associations. If there's also a Song\n\t * model defined in Album with private modifier, maybe the album just have\n\t * one song, they are one2one association too.\n\t * \n\t * When it's many2one association, it's easy to just simply add a foreign id\n\t * column to the many side model's table. But when it comes to many2many\n\t * association, it can not be done without intermediate join table in\n\t * database. LitePal assumes that this join table's name is the\n\t * concatenation of the two target table names in alphabetical order.\n\t * \n\t * @param className\n\t *            Source class name.\n\t * @param field\n\t *            A field of source class.\n\t * @param action\n\t *            Between {@link org.litepal.LitePalBase#GET_ASSOCIATIONS_ACTION} and\n\t *            {@link org.litepal.LitePalBase#GET_ASSOCIATION_INFO_ACTION}\n\t */\n\tprivate void oneToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {\n\t\tClass<?> fieldTypeClass = field.getType();\n\t\t// If the mapping list contains the class name\n\t\t// defined in one class.\n\t\tif (LitePalAttr.getInstance().getClassNames().contains(fieldTypeClass.getName())) {\n\t\t\tClass<?> reverseDynamicClass = Class.forName(fieldTypeClass.getName());\n\t\t\tField[] reverseFields = reverseDynamicClass.getDeclaredFields();\n\t\t\t// Look up if there's a reverse association\n\t\t\t// definition in the reverse class.\n\t\t\tboolean reverseAssociations = false;\n\t\t\t// Begin to check the fields of the defined\n\t\t\t// class.\n            for (Field reverseField : reverseFields) {\n                if (!Modifier.isStatic(reverseField.getModifiers())) {\n                    Class<?> reverseFieldTypeClass = reverseField.getType();\n                    // If there's the from class name in the\n                    // defined class, they are one2one bidirectional\n                    // associations.\n                    if (className.equals(reverseFieldTypeClass.getName())) {\n                        if (action == GET_ASSOCIATIONS_ACTION) {\n                            addIntoAssociationModelCollection(className, fieldTypeClass.getName(),\n                                    fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);\n                        } else if (action == GET_ASSOCIATION_INFO_ACTION) {\n                            addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),\n                                    fieldTypeClass.getName(), field, reverseField, Const.Model.ONE_TO_ONE);\n                        }\n                        reverseAssociations = true;\n                    }\n                    // If there's the from class Set or List in\n                    // the defined class, they are many2one bidirectional\n                    // associations.\n                    else if (isCollection(reverseFieldTypeClass)) {\n                        String genericTypeName = getGenericTypeName(reverseField);\n                        if (className.equals(genericTypeName)) {\n                            if (action == GET_ASSOCIATIONS_ACTION) {\n                                addIntoAssociationModelCollection(className, fieldTypeClass.getName(),\n                                        className, Const.Model.MANY_TO_ONE);\n                            } else if (action == GET_ASSOCIATION_INFO_ACTION) {\n                                addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),\n                                        className, field, reverseField, Const.Model.MANY_TO_ONE);\n                            }\n                            reverseAssociations = true;\n                        }\n                    }\n                }\n            }\n            // If there's no from class in the defined class, they are\n            // one2one unidirectional associations.\n            if (!reverseAssociations) {\n                if (action == GET_ASSOCIATIONS_ACTION) {\n                    addIntoAssociationModelCollection(className, fieldTypeClass.getName(),\n                            fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);\n                } else if (action == GET_ASSOCIATION_INFO_ACTION) {\n                    addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),\n                            fieldTypeClass.getName(), field, null, Const.Model.ONE_TO_ONE);\n                }\n            }\n\t\t}\n\t}\n\n\t/**\n\t * Deals with one to any association conditions. e.g. Song and Album. An\n\t * album have many songs, and a song belongs to one album. So if there's an\n\t * Album model defined in Song with private modifier, and in Album there's a\n\t * List or Set with generic type of Song and declared as private modifier,\n\t * they are one2many association. If there's no List or Set defined in\n\t * Album, they will become one2one associations. If there's also a Song\n\t * model defined in Album with private modifier, maybe the album just have\n\t * one song, they are one2one association too.\n\t * \n\t * When it's many2one association, it's easy to just simply add a foreign id\n\t * column to the many side model's table. But when it comes to many2many\n\t * association, it can not be done without intermediate join table in\n\t * database. LitePal assumes that this join table's name is the\n\t * concatenation of the two target table names in alphabetical order.\n\t * \n\t * @param className\n\t *            Source class name.\n\t * @param field\n\t *            A field of source class.\n\t * @param action\n\t *            Between {@link org.litepal.LitePalBase#GET_ASSOCIATIONS_ACTION} and\n\t *            {@link org.litepal.LitePalBase#GET_ASSOCIATION_INFO_ACTION}\n\t */\n\tprivate void manyToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {\n\t\tif (isCollection(field.getType())) {\n\t\t\tString genericTypeName = getGenericTypeName(field);\n\t\t\t// If the mapping list contains the genericTypeName, begin to check\n\t\t\t// this genericTypeName class.\n\t\t\tif (LitePalAttr.getInstance().getClassNames().contains(genericTypeName)) {\n\t\t\t\tClass<?> reverseDynamicClass = Class.forName(genericTypeName);\n\t\t\t\tField[] reverseFields = reverseDynamicClass.getDeclaredFields();\n\t\t\t\t// Look up if there's a reverse association\n\t\t\t\t// definition in the reverse class.\n\t\t\t\tboolean reverseAssociations = false;\n                for (Field reverseField : reverseFields) {\n                    // Only map private fields\n                    if (!Modifier.isStatic(reverseField.getModifiers())) {\n                        Class<?> reverseFieldTypeClass = reverseField.getType();\n                        // If there's a from class name defined in the reverse\n                        // class, they are many2one bidirectional\n                        // associations.\n                        if (className.equals(reverseFieldTypeClass.getName())) {\n                            if (action == GET_ASSOCIATIONS_ACTION) {\n                                addIntoAssociationModelCollection(className, genericTypeName,\n                                        genericTypeName, Const.Model.MANY_TO_ONE);\n                            } else if (action == GET_ASSOCIATION_INFO_ACTION) {\n                                addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,\n                                        field, reverseField, Const.Model.MANY_TO_ONE);\n                            }\n                            reverseAssociations = true;\n                        }\n                        // If there's a List or Set contains from class name\n                        // defined in the reverse class, they are many2many\n                        // association.\n                        else if (isCollection(reverseFieldTypeClass)) {\n                            String reverseGenericTypeName = getGenericTypeName(reverseField);\n                            if (className.equals(reverseGenericTypeName)) {\n                                if (action == GET_ASSOCIATIONS_ACTION) {\n                                    if (className.equalsIgnoreCase(genericTypeName)) {\n                                        // This is M2M self association condition. Regard as generic model condition.\n                                        GenericModel genericModel = new GenericModel();\n                                        genericModel.setTableName(DBUtility.getGenericTableName(className, field.getName()));\n                                        genericModel.setValueColumnName(DBUtility.getM2MSelfRefColumnName(field));\n                                        genericModel.setValueColumnType(\"integer\");\n                                        genericModel.setValueIdColumnName(DBUtility.getGenericValueIdColumnName(className));\n                                        mGenericModels.add(genericModel);\n                                    } else {\n                                        addIntoAssociationModelCollection(className, genericTypeName, null,\n                                                Const.Model.MANY_TO_MANY);\n                                    }\n                                } else if (action == GET_ASSOCIATION_INFO_ACTION) {\n                                    if (!className.equalsIgnoreCase(genericTypeName)) {\n                                        addIntoAssociationInfoCollection(className, genericTypeName, null, field,\n                                                reverseField, Const.Model.MANY_TO_MANY);\n                                    }\n                                }\n                                reverseAssociations = true;\n                            }\n                        }\n                    }\n                }\n                // If there's no from class in the defined class, they\n                // are many2one unidirectional associations.\n                if (!reverseAssociations) {\n                    if (action == GET_ASSOCIATIONS_ACTION) {\n                        addIntoAssociationModelCollection(className, genericTypeName,\n                                genericTypeName, Const.Model.MANY_TO_ONE);\n                    } else if (action == GET_ASSOCIATION_INFO_ACTION) {\n                        addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,\n                                field, null, Const.Model.MANY_TO_ONE);\n                    }\n                }\n\t\t\t} else if(BaseUtility.isGenericTypeSupported(genericTypeName) && action == GET_ASSOCIATIONS_ACTION) {\n                GenericModel genericModel = new GenericModel();\n                genericModel.setTableName(DBUtility.getGenericTableName(className, field.getName()));\n                genericModel.setValueColumnName(DBUtility.convertToValidColumnName(field.getName()));\n                genericModel.setValueColumnType(getColumnType(genericTypeName));\n                genericModel.setValueIdColumnName(DBUtility.getGenericValueIdColumnName(className));\n                mGenericModels.add(genericModel);\n            }\n\t\t}\n\t}\n\n\t/**\n\t * Package a {@link org.litepal.tablemanager.model.AssociationsModel}, and add it into\n\t * {@link #mAssociationModels} Collection.\n\t * \n\t * @param className\n\t *            The class name for {@link org.litepal.tablemanager.model.AssociationsModel}.\n\t * @param associatedClassName\n\t *            The associated class name for {@link org.litepal.tablemanager.model.AssociationsModel}.\n\t * @param classHoldsForeignKey\n\t *            The class which holds foreign key.\n\t * @param associationType\n\t *            The association type for {@link org.litepal.tablemanager.model.AssociationsModel}.\n\t */\n\tprivate void addIntoAssociationModelCollection(String className, String associatedClassName,\n\t\t\tString classHoldsForeignKey, int associationType) {\n\t\tAssociationsModel associationModel = new AssociationsModel();\n\t\tassociationModel.setTableName(DBUtility.getTableNameByClassName(className));\n\t\tassociationModel.setAssociatedTableName(DBUtility.getTableNameByClassName(associatedClassName));\n\t\tassociationModel.setTableHoldsForeignKey(DBUtility.getTableNameByClassName(classHoldsForeignKey));\n\t\tassociationModel.setAssociationType(associationType);\n\t\tmAssociationModels.add(associationModel);\n\t}\n\n\t/**\n\t * Package a {@link org.litepal.crud.model.AssociationsInfo}, and add it into\n\t * {@link #mAssociationInfos} Collection.\n\t * \n\t * @param selfClassName\n\t *            The class name of self model.\n\t * @param associatedClassName\n\t *            The class name of the class which associated with self class.\n\t * @param classHoldsForeignKey\n\t *            The class which holds foreign key.\n\t * @param associateOtherModelFromSelf\n\t *            The field of self class to declare has association with other\n\t *            class.\n\t * @param associateSelfFromOtherModel\n\t *            The field of the associated class to declare has association\n\t *            with self class.\n\t * @param associationType\n\t *            The association type.\n\t */\n\tprivate void addIntoAssociationInfoCollection(String selfClassName, String associatedClassName,\n\t\t\tString classHoldsForeignKey, Field associateOtherModelFromSelf,\n\t\t\tField associateSelfFromOtherModel, int associationType) {\n\t\tAssociationsInfo associationInfo = new AssociationsInfo();\n\t\tassociationInfo.setSelfClassName(selfClassName);\n\t\tassociationInfo.setAssociatedClassName(associatedClassName);\n\t\tassociationInfo.setClassHoldsForeignKey(classHoldsForeignKey);\n\t\tassociationInfo.setAssociateOtherModelFromSelf(associateOtherModelFromSelf);\n\t\tassociationInfo.setAssociateSelfFromOtherModel(associateSelfFromOtherModel);\n\t\tassociationInfo.setAssociationType(associationType);\n\t\tmAssociationInfos.add(associationInfo);\n\t}\n\n\t/**\n\t * Get the generic type name of List or Set. If there's no generic type of\n\t * List or Set return null.\n\t * \n\t * @param field\n\t *            A generic type field.\n\t * @return The name of generic type of List of Set.\n\t */\n\tprotected String getGenericTypeName(Field field) {\n\t\tClass<?> genericTypeClass = getGenericTypeClass(field);\n        if (genericTypeClass != null) {\n            return genericTypeClass.getName();\n        }\n        return null;\n\t}\n\n    /**\n     * Convert a field instance into A ColumnModel instance. ColumnModel can provide information\n     * when creating table.\n     * @param field\n     *          A supported field to map into column.\n     * @return ColumnModel instance contains column information.\n     */\n    private ColumnModel convertFieldToColumnModel(Field field) {\n        String fieldType = field.getType().getName();\n        String columnType = getColumnType(fieldType);\n        boolean nullable = true;\n        boolean unique = false;\n        boolean hasIndex = false;\n        String defaultValue = \"\";\n        Column annotation = field.getAnnotation(Column.class);\n        if (annotation != null) {\n            nullable = annotation.nullable();\n            unique = annotation.unique();\n            defaultValue = annotation.defaultValue();\n            hasIndex = annotation.index();\n        }\n        ColumnModel columnModel = new ColumnModel();\n        columnModel.setColumnName(DBUtility.convertToValidColumnName(field.getName()));\n        columnModel.setColumnType(columnType);\n        columnModel.setNullable(nullable);\n        columnModel.setUnique(unique);\n        columnModel.setDefaultValue(defaultValue);\n        columnModel.setHasIndex(hasIndex);\n        return columnModel;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/LitePalDB.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal;\n\nimport org.litepal.parser.LitePalAttr;\nimport org.litepal.parser.LitePalConfig;\nimport org.litepal.parser.LitePalParser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Configuration of LitePal database. It's similar to litepal.xml configuration, but allows to\n * configure database details at runtime. This is very important when comes to support multiple\n * databases functionality.\n *\n * @author Tony Green\n * @since 1.4\n */\npublic class LitePalDB {\n\n    /**\n     * The version of database.\n     */\n    private int version;\n\n    /**\n     * The name of database.\n     */\n    private String dbName;\n\n    /**\n     * Define where the .db file should be. Option values: internal, external, or path in sdcard.\n     */\n    private String storage;\n\n    /**\n     * Indicates that the database file stores in external storage or not.\n     */\n    private boolean isExternalStorage = false;\n\n    /**\n     * All the model classes that want to map in the database. Each class should\n     * be given the full name including package name.\n     */\n    private List<String> classNames;\n\n    /**\n     * Construct a LitePalDB instance from the default configuration by litepal.xml. But database\n     * name must be different than the default.\n     * @param dbName\n     *          Name of database.\n     * @return A LitePalDB instance which used the default configuration in litepal.xml but with a specified database name.\n     */\n    public static LitePalDB fromDefault(String dbName) {\n        LitePalConfig config = LitePalParser.parseLitePalConfiguration();\n        LitePalDB litePalDB = new LitePalDB(dbName, config.getVersion());\n        litePalDB.setStorage(config.getStorage());\n        litePalDB.setClassNames(config.getClassNames());\n        return litePalDB;\n    }\n\n    /**\n     * Construct a LitePalDB instance. Database name and version are necessary fields.\n     * @param dbName\n     *          Name of database.\n     * @param version\n     *          Version of database.\n     */\n    public LitePalDB(String dbName, int version) {\n        this.dbName = dbName;\n        this.version = version;\n    }\n\n    public int getVersion() {\n        return version;\n    }\n\n    public String getDbName() {\n        return dbName;\n    }\n\n    public String getStorage() {\n        return storage;\n    }\n\n    public void setStorage(String storage) {\n        this.storage = storage;\n    }\n\n    public boolean isExternalStorage() {\n        return isExternalStorage;\n    }\n\n    public void setExternalStorage(boolean isExternalStorage) {\n        this.isExternalStorage = isExternalStorage;\n    }\n\n    /**\n     * Get the class name list. Always add table_schema as a value.\n     *\n     * @return The class name list.\n     */\n    public List<String> getClassNames() {\n        if (classNames == null) {\n            classNames = new ArrayList<String>();\n            classNames.add(\"org.litepal.model.Table_Schema\");\n        } else if (classNames.isEmpty()) {\n            classNames.add(\"org.litepal.model.Table_Schema\");\n        }\n        return classNames;\n    }\n\n    /**\n     * Add a class name into the current mapping model list.\n     *\n     * @param className\n     *            Full package class name.\n     */\n    public void addClassName(String className) {\n        getClassNames().add(className);\n    }\n\n    void setClassNames(List<String> className) {\n        this.classNames = className;\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/Operator.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.text.TextUtils;\n\nimport org.litepal.crud.DeleteHandler;\nimport org.litepal.crud.LitePalSupport;\nimport org.litepal.crud.QueryHandler;\nimport org.litepal.crud.SaveHandler;\nimport org.litepal.crud.UpdateHandler;\nimport org.litepal.crud.async.AverageExecutor;\nimport org.litepal.crud.async.CountExecutor;\nimport org.litepal.crud.async.FindExecutor;\nimport org.litepal.crud.async.FindMultiExecutor;\nimport org.litepal.crud.async.SaveExecutor;\nimport org.litepal.crud.async.UpdateOrDeleteExecutor;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.parser.LitePalAttr;\nimport org.litepal.parser.LitePalConfig;\nimport org.litepal.parser.LitePalParser;\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.tablemanager.callback.DatabaseListener;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.Const;\nimport org.litepal.util.DBUtility;\nimport org.litepal.util.SharedUtil;\nimport org.litepal.util.cipher.CipherUtil;\n\nimport java.io.File;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * LitePal is an Android library that allows developers to use SQLite database extremely easy.\n * You can initialized it by calling {@link #initialize(Context)} method to make LitePal ready to\n * work. Also you can switch the using database by calling {@link #use(LitePalDB)} and {@link #useDefault()}\n * methods.\n *\n * @author Tony Green\n * @since 2.1\n */\npublic class Operator {\n\n    private static Handler handler = new Handler(Looper.getMainLooper());\n\n    private static DatabaseListener dbListener = null;\n\n    /**\n     * Get the main thread handler. You don't need this method. It's used by framework only.\n     * @return Main thread handler.\n     */\n    public static Handler getHandler() {\n        return handler;\n    }\n\n    /**\n     * Initialize to make LitePal ready to work. If you didn't configure LitePalApplication\n     * in the AndroidManifest.xml, make sure you call this method as soon as possible. In\n     * Application's onCreate() method will be fine.\n     *\n     * @param context\n     * \t\tApplication context.\n     */\n    public static void initialize(Context context) {\n        LitePalApplication.sContext = context;\n    }\n\n    /**\n     * Get a writable SQLiteDatabase.\n     *\n     * @return A writable SQLiteDatabase instance\n     */\n    public static SQLiteDatabase getDatabase() {\n        return Connector.getDatabase();\n    }\n\n    /**\n     * Begins a transaction in EXCLUSIVE mode.\n     */\n    public static void beginTransaction() {\n        getDatabase().beginTransaction();\n    }\n\n    /**\n     * End a transaction.\n     */\n    public static void endTransaction() {\n        getDatabase().endTransaction();\n    }\n\n    /**\n     * Marks the current transaction as successful. Do not do any more database work between calling this and calling endTransaction.\n     * Do as little non-database work as possible in that situation too.\n     * If any errors are encountered between this and endTransaction the transaction will still be committed.\n     */\n    public static void setTransactionSuccessful() {\n        getDatabase().setTransactionSuccessful();\n    }\n\n    /**\n     * Switch the using database to the one specified by parameter.\n     * @param litePalDB\n     *          The database to switch to.\n     */\n    public static void use(LitePalDB litePalDB) {\n        synchronized (LitePalSupport.class) {\n            LitePalAttr litePalAttr = LitePalAttr.getInstance();\n            litePalAttr.setDbName(litePalDB.getDbName());\n            litePalAttr.setVersion(litePalDB.getVersion());\n            litePalAttr.setStorage(litePalDB.getStorage());\n            litePalAttr.setClassNames(litePalDB.getClassNames());\n            // set the extra key name only when use database other than default or litepal.xml not exists\n            if (!isDefaultDatabase(litePalDB.getDbName())) {\n                litePalAttr.setExtraKeyName(litePalDB.getDbName());\n                litePalAttr.setCases(\"lower\");\n            }\n            Connector.clearLitePalOpenHelperInstance();\n        }\n    }\n\n    /**\n     * Switch the using database to default with configuration by litepal.xml.\n     */\n    public static void useDefault() {\n        synchronized (LitePalSupport.class) {\n            LitePalAttr.clearInstance();\n            Connector.clearLitePalOpenHelperInstance();\n        }\n    }\n\n    /**\n     * Delete the specified database.\n     * @param dbName\n     *          Name of database to delete.\n     * @return True if delete success, false otherwise.\n     */\n    public static boolean deleteDatabase(String dbName) {\n        synchronized (LitePalSupport.class) {\n            if (!TextUtils.isEmpty(dbName)) {\n                if (!dbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {\n                    dbName = dbName + Const.Config.DB_NAME_SUFFIX;\n                }\n                File dbFile = LitePalApplication.getContext().getDatabasePath(dbName);\n                if (dbFile.exists()) {\n                    boolean result = dbFile.delete();\n                    if (result) {\n                        removeVersionInSharedPreferences(dbName);\n                        Connector.clearLitePalOpenHelperInstance();\n                    }\n                    return result;\n                }\n                String path = LitePalApplication.getContext().getExternalFilesDir(\"\") + \"/databases/\";\n                dbFile = new File(path + dbName);\n                boolean result = dbFile.delete();\n                if (result) {\n                    removeVersionInSharedPreferences(dbName);\n                    Connector.clearLitePalOpenHelperInstance();\n                }\n                return result;\n            }\n            return false;\n        }\n    }\n\n    public static void aesKey(String key) {\n        CipherUtil.aesKey = key;\n    }\n\n    /**\n     * Remove the database version in SharedPreferences file.\n     * @param dbName\n     *          Name of database to delete.\n     */\n    private static void removeVersionInSharedPreferences(String dbName) {\n        if (isDefaultDatabase(dbName)) {\n            SharedUtil.removeVersion(null);\n        } else {\n            SharedUtil.removeVersion(dbName);\n        }\n    }\n\n    /**\n     * Check the dbName is default database or not. If it's same as dbName in litepal.xml, then it is\n     * default database.\n     * @param dbName\n     *          Name of database to check.\n     * @return True if it's default database, false otherwise.\n     */\n    private static boolean isDefaultDatabase(String dbName) {\n        if (BaseUtility.isLitePalXMLExists()) {\n            if (!dbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {\n                dbName = dbName + Const.Config.DB_NAME_SUFFIX;\n            }\n            LitePalConfig config = LitePalParser.parseLitePalConfiguration();\n            String defaultDbName = config.getDbName();\n            if (!defaultDbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {\n                defaultDbName = defaultDbName + Const.Config.DB_NAME_SUFFIX;\n            }\n            return dbName.equalsIgnoreCase(defaultDbName);\n        }\n        return false;\n    }\n\n    /**\n     * Declaring to query which columns in table.\n     *\n     * <pre>\n     * LitePal.select(&quot;name&quot;, &quot;age&quot;).find(Person.class);\n     * </pre>\n     *\n     * This will find all rows with name and age columns in Person table.\n     *\n     * @param columns\n     *            A String array of which columns to return. Passing null will\n     *            return all columns.\n     *\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery select(String... columns) {\n        FluentQuery cQuery = new FluentQuery();\n        cQuery.mColumns = columns;\n        return cQuery;\n    }\n\n    /**\n     * Declaring to query which rows in table.\n     *\n     * <pre>\n     * LitePal.where(&quot;name = ? or age &gt; ?&quot;, &quot;Tom&quot;, &quot;14&quot;).find(Person.class);\n     * </pre>\n     *\n     * This will find rows which name is Tom or age greater than 14 in Person\n     * table.\n     *\n     * @param conditions\n     *            A filter declaring which rows to return, formatted as an SQL\n     *            WHERE clause. Passing null will return all rows.\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery where(String... conditions) {\n        FluentQuery cQuery = new FluentQuery();\n        cQuery.mConditions = conditions;\n        return cQuery;\n    }\n\n    /**\n     * Declaring how to order the rows queried from table.\n     *\n     * <pre>\n     * LitePal.order(&quot;name desc&quot;).find(Person.class);\n     * </pre>\n     *\n     * This will find all rows in Person table sorted by name with inverted\n     * order.\n     *\n     * @param column\n     *            How to order the rows, formatted as an SQL ORDER BY clause.\n     *            Passing null will use the default sort order, which may be\n     *            unordered.\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery order(String column) {\n        FluentQuery cQuery = new FluentQuery();\n        cQuery.mOrderBy = column;\n        return cQuery;\n    }\n\n    /**\n     * Limits the number of rows returned by the query.\n     *\n     * <pre>\n     * LitePal.limit(2).find(Person.class);\n     * </pre>\n     *\n     * This will find the top 2 rows in Person table.\n     *\n     * @param value\n     *            Limits the number of rows returned by the query, formatted as\n     *            LIMIT clause.\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery limit(int value) {\n        FluentQuery cQuery = new FluentQuery();\n        cQuery.mLimit = String.valueOf(value);\n        return cQuery;\n    }\n\n    /**\n     * Declaring the offset of rows returned by the query. This method must be\n     * used with {@link #limit(int)}, or nothing will return.\n     *\n     * <pre>\n     * LitePal.limit(1).offset(2).find(Person.class);\n     * </pre>\n     *\n     * This will find the third row in Person table.\n     *\n     * @param value\n     *            The offset amount of rows returned by the query.\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery offset(int value) {\n        FluentQuery cQuery = new FluentQuery();\n        cQuery.mOffset = String.valueOf(value);\n        return cQuery;\n    }\n\n    /**\n     * Count the records.\n     *\n     * <pre>\n     * LitePal.count(Person.class);\n     * </pre>\n     *\n     * This will count all rows in person table.<br>\n     * You can also specify a where clause when counting.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(Person.class);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @return Count of the specified table.\n     */\n    public static int count(Class<?> modelClass) {\n        return count(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())));\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static CountExecutor countAsync(final Class<?> modelClass) {\n        return countAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())));\n    }\n\n    /**\n     * Count the records.\n     *\n     * <pre>\n     * LitePal.count(&quot;person&quot;);\n     * </pre>\n     *\n     * This will count all rows in person table.<br>\n     * You can also specify a where clause when counting.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(&quot;person&quot;);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @return Count of the specified table.\n     */\n    public static int count(String tableName) {\n        synchronized (LitePalSupport.class) {\n            FluentQuery cQuery = new FluentQuery();\n            return cQuery.count(tableName);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static CountExecutor countAsync(final String tableName) {\n        final CountExecutor executor = new CountExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int count = count(tableName);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(count);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Calculates the average value on a given column.\n     *\n     * <pre>\n     * LitePal.average(Person.class, &quot;age&quot;);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(Person.class, &quot;age&quot;);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param column\n     *            The based on column to calculate.\n     * @return The average value on a given column.\n     */\n    public static double average(Class<?> modelClass, String column) {\n        return average(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), column);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static AverageExecutor averageAsync(final Class<?> modelClass, final String column) {\n        return averageAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), column);\n    }\n\n    /**\n     * Calculates the average value on a given column.\n     *\n     * <pre>\n     * LitePal.average(&quot;person&quot;, &quot;age&quot;);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(&quot;person&quot;, &quot;age&quot;);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param column\n     *            The based on column to calculate.\n     * @return The average value on a given column.\n     */\n    public static double average(String tableName, String column) {\n        synchronized (LitePalSupport.class) {\n            FluentQuery cQuery = new FluentQuery();\n            return cQuery.average(tableName, column);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static AverageExecutor averageAsync(final String tableName, final String column) {\n        final AverageExecutor executor = new AverageExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final double average = average(tableName, column);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(average);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Calculates the maximum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.max(Person.class, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(Person.class, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The maximum value on a given column.\n     */\n    public static <T> T max(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return max(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> maxAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return maxAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n    /**\n     * Calculates the maximum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.max(&quot;person&quot;, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The maximum value on a given column.\n     */\n    public static <T> T max(String tableName, String columnName, Class<T> columnType) {\n        synchronized (LitePalSupport.class) {\n            FluentQuery cQuery = new FluentQuery();\n            return cQuery.max(tableName, columnName, columnType);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> maxAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = max(tableName, columnName, columnType);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Calculates the minimum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.min(Person.class, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(Person.class, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The minimum value on a given column.\n     */\n    public static <T> T min(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return min(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> minAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return minAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n    /**\n     * Calculates the minimum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.min(&quot;person&quot;, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The minimum value on a given column.\n     */\n    public static <T> T min(String tableName, String columnName, Class<T> columnType) {\n        synchronized (LitePalSupport.class) {\n            FluentQuery cQuery = new FluentQuery();\n            return cQuery.min(tableName, columnName, columnType);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> minAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = min(tableName, columnName, columnType);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Calculates the sum of values on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.sum(Person.class, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(Person.class, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The sum value on a given column.\n     */\n    public static <T> T sum(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return sum(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> sumAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return sumAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);\n    }\n\n    /**\n     * Calculates the sum of values on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.sum(&quot;person&quot;, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The sum value on a given column.\n     */\n    public static <T> T sum(String tableName, String columnName, Class<T> columnType) {\n        synchronized (LitePalSupport.class) {\n            FluentQuery cQuery = new FluentQuery();\n            return cQuery.sum(tableName, columnName, columnType);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> sumAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = sum(tableName, columnName, columnType);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Finds the record by a specific id.\n     *\n     * <pre>\n     * Person p = LitePal.find(Person.class, 1);\n     * </pre>\n     *\n     * The modelClass determines which table to query and the object type to\n     * return. If no record can be found, then return null. <br>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link Operator#find(Class, long, boolean)}.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param id\n     *            Which record to query.\n     * @return An object with found data from database, or null.\n     */\n    public static <T> T find(Class<T> modelClass, long id) {\n        return find(modelClass, id, false);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> findAsync(Class<T> modelClass, long id) {\n        return findAsync(modelClass, id, false);\n    }\n\n    /**\n     * It is mostly same as {@link Operator#find(Class, long)} but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param id\n     *            Which record to query.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return An object with found data from database, or null.\n     */\n    public static <T> T find(Class<T> modelClass, long id, boolean isEager) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onFind(modelClass, id, isEager);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> findAsync(final Class<T> modelClass, final long id, final boolean isEager) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = find(modelClass, id, isEager);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Finds the first record of a single table.\n     *\n     * <pre>\n     * Person p = LitePal.findFirst(Person.class);\n     * </pre>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link Operator#findFirst(Class, boolean)}.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @return An object with data of first row, or null.\n     */\n    public static <T> T findFirst(Class<T> modelClass) {\n        return findFirst(modelClass, false);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> findFirstAsync(Class<T> modelClass) {\n        return findFirstAsync(modelClass, false);\n    }\n\n    /**\n     * It is mostly same as {@link Operator#findFirst(Class)} but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return An object with data of first row, or null.\n     */\n    public static <T> T findFirst(Class<T> modelClass, boolean isEager) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onFindFirst(modelClass, isEager);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> findFirstAsync(final Class<T> modelClass, final boolean isEager) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = findFirst(modelClass, isEager);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Finds the last record of a single table.\n     *\n     * <pre>\n     * Person p = LitePal.findLast(Person.class);\n     * </pre>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link Operator#findLast(Class, boolean)}.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @return An object with data of last row, or null.\n     */\n    public static <T> T findLast(Class<T> modelClass) {\n        return findLast(modelClass, false);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> findLastAsync(Class<T> modelClass) {\n        return findLastAsync(modelClass, false);\n    }\n\n    /**\n     * It is mostly same as {@link Operator#findLast(Class)} but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return An object with data of last row, or null.\n     */\n    public static <T> T findLast(Class<T> modelClass, boolean isEager) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onFindLast(modelClass, isEager);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindExecutor<T> findLastAsync(final Class<T> modelClass, final boolean isEager) {\n        final FindExecutor<T> executor = new FindExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final T t = findLast(modelClass, isEager);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Finds multiple records by an id array.\n     *\n     * <pre>\n     * List&lt;Person&gt; people = LitePal.findAll(Person.class, 1, 2, 3);\n     *\n     * long[] bookIds = { 10, 18 };\n     * List&lt;Book&gt; books = LitePal.findAll(Book.class, bookIds);\n     * </pre>\n     *\n     * Of course you can find all records by passing nothing to the ids\n     * parameter.\n     *\n     * <pre>\n     * List&lt;Book&gt; allBooks = LitePal.findAll(Book.class);\n     * </pre>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link Operator#findAll(Class, boolean, long...)}.\n     *\n     * The modelClass determines which table to query and the object type to\n     * return.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return as a list.\n     * @param ids\n     *            Which records to query. Or do not pass it to find all records.\n     * @return An object list with found data from database, or an empty list.\n     */\n    public static <T> List<T> findAll(Class<T> modelClass, long... ids) {\n        return findAll(modelClass, false, ids);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindMultiExecutor<T> findAllAsync(Class<T> modelClass, long... ids) {\n        return findAllAsync(modelClass, false, ids);\n    }\n\n    /**\n     * It is mostly same as {@link Operator#findAll(Class, long...)} but an\n     * isEager parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return as a list.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @param ids\n     *            Which records to query. Or do not pass it to find all records.\n     * @return An object list with found data from database, or an empty list.\n     */\n    public static <T> List<T> findAll(Class<T> modelClass, boolean isEager,\n                                      long... ids) {\n        synchronized (LitePalSupport.class) {\n            QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());\n            return queryHandler.onFindAll(modelClass, isEager, ids);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T> FindMultiExecutor<T> findAllAsync(final Class<T> modelClass, final boolean isEager, final long... ids) {\n        final FindMultiExecutor<T> executor = new FindMultiExecutor<>();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final List<T> t = findAll(modelClass, isEager, ids);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(t);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Runs the provided SQL and returns a Cursor over the result set. You may\n     * include ? in where clause in the query, which will be replaced by the\n     * second to the last parameters, such as:\n     *\n     * <pre>\n     * Cursor cursor = LitePal.findBySQL(&quot;select * from person where name=? and age=?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     * </pre>\n     *\n     * @param sql\n     *            First parameter is the SQL clause to apply. Second to the last\n     *            parameters will replace the place holders.\n     * @return A Cursor object, which is positioned before the first entry. Note\n     *         that Cursors are not synchronized, see the documentation for more\n     *         details.\n     */\n    public static Cursor findBySQL(String... sql) {\n        synchronized (LitePalSupport.class) {\n            BaseUtility.checkConditionsCorrect(sql);\n            if (sql == null) {\n                return null;\n            }\n            if (sql.length <= 0) {\n                return null;\n            }\n            String[] selectionArgs;\n            if (sql.length == 1) {\n                selectionArgs = null;\n            } else {\n                selectionArgs = new String[sql.length - 1];\n                System.arraycopy(sql, 1, selectionArgs, 0, sql.length - 1);\n            }\n            return Connector.getDatabase().rawQuery(sql[0], selectionArgs);\n        }\n    }\n\n    /**\n     * Deletes the record in the database by id.<br>\n     * The data in other tables which is referenced with the record will be\n     * removed too.\n     *\n     * <pre>\n     * LitePal.delete(Person.class, 1);\n     * </pre>\n     *\n     * This means that the record 1 in person table will be removed.\n     *\n     * @param modelClass\n     *            Which table to delete from by class.\n     * @param id\n     *            Which record to delete.\n     * @return The number of rows affected. Including cascade delete rows.\n     */\n    public static int delete(Class<?> modelClass, long id) {\n        synchronized (LitePalSupport.class) {\n            int rowsAffected;\n            SQLiteDatabase db = Connector.getDatabase();\n            db.beginTransaction();\n            try {\n                DeleteHandler deleteHandler = new DeleteHandler(db);\n                rowsAffected = deleteHandler.onDelete(modelClass, id);\n                db.setTransactionSuccessful();\n                return rowsAffected;\n            } finally {\n                db.endTransaction();\n            }\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static UpdateOrDeleteExecutor deleteAsync(final Class<?> modelClass, final long id) {\n        final UpdateOrDeleteExecutor executor = new UpdateOrDeleteExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int rowsAffected = delete(modelClass, id);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(rowsAffected);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Deletes all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL DELETE statement and sends\n     * it to the database.\n     *\n     * <pre>\n     * LitePal.deleteAll(Person.class, &quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     * </pre>\n     *\n     * This means that all the records which name is Tom and age is 14 will be\n     * removed.<br>\n     *\n     * @param modelClass\n     *            Which table to delete from by class.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            deleting. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return The number of rows affected.\n     */\n    public static int deleteAll(Class<?> modelClass, String... conditions) {\n        synchronized (LitePalSupport.class) {\n            int rowsAffected;\n            SQLiteDatabase db = Connector.getDatabase();\n            db.beginTransaction();\n            try {\n                DeleteHandler deleteHandler = new DeleteHandler(db);\n                rowsAffected = deleteHandler.onDeleteAll(modelClass, conditions);\n                db.setTransactionSuccessful();\n                return rowsAffected;\n            } finally {\n                db.endTransaction();\n            }\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static UpdateOrDeleteExecutor deleteAllAsync(final Class<?> modelClass, final String... conditions) {\n        final UpdateOrDeleteExecutor executor = new UpdateOrDeleteExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int rowsAffected = deleteAll(modelClass, conditions);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(rowsAffected);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Deletes all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL DELETE statement and sends\n     * it to the database.\n     *\n     * <pre>\n     * LitePal.deleteAll(&quot;person&quot;, &quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     * </pre>\n     *\n     * This means that all the records which name is Tom and age is 14 will be\n     * removed.<br>\n     *\n     * Note that this method won't delete the referenced data in other tables.\n     * You should remove those values by your own.\n     *\n     * @param tableName\n     *            Which table to delete from.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            deleting. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return The number of rows affected.\n     */\n    public static int deleteAll(String tableName, String... conditions) {\n        synchronized (LitePalSupport.class) {\n            DeleteHandler deleteHandler = new DeleteHandler(Connector.getDatabase());\n            return deleteHandler.onDeleteAll(tableName, conditions);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static UpdateOrDeleteExecutor deleteAllAsync(final String tableName, final String... conditions) {\n        final UpdateOrDeleteExecutor executor = new UpdateOrDeleteExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int rowsAffected = deleteAll(tableName, conditions);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(rowsAffected);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Updates the corresponding record by id with ContentValues. Returns the\n     * number of affected rows.\n     *\n     * <pre>\n     * ContentValues cv = new ContentValues();\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     * LitePal.update(Person.class, cv, 1);\n     * </pre>\n     *\n     * This means that the name of record 1 will be updated into Jim.<br>\n     *\n     * @param modelClass\n     *            Which table to update by class.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param id\n     *            Which record to update.\n     * @return The number of rows affected.\n     */\n    public static int update(Class<?> modelClass, ContentValues values, long id) {\n        synchronized (LitePalSupport.class) {\n            UpdateHandler updateHandler = new UpdateHandler(Connector.getDatabase());\n            return updateHandler.onUpdate(modelClass, id, values);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static UpdateOrDeleteExecutor updateAsync(final Class<?> modelClass, final ContentValues values, final long id) {\n        final UpdateOrDeleteExecutor executor = new UpdateOrDeleteExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int rowsAffected = update(modelClass, values, id);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(rowsAffected);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Updates all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL UPDATE statement and sends\n     * it to the database.\n     *\n     * <pre>\n     * ContentValues cv = new ContentValues();\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     * LitePal.update(Person.class, cv, &quot;name = ?&quot;, &quot;Tom&quot;);\n     * </pre>\n     *\n     * This means that all the records which name is Tom will be updated into\n     * Jim.\n     *\n     * @param modelClass\n     *            Which table to update by class.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            updating. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return The number of rows affected.\n     */\n    public static int updateAll(Class<?> modelClass, ContentValues values,\n                                String... conditions) {\n        return updateAll(BaseUtility.changeCase(DBUtility.getTableNameByClassName(\n                modelClass.getName())), values, conditions);\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static UpdateOrDeleteExecutor updateAllAsync(Class<?> modelClass, ContentValues values, String... conditions) {\n        return updateAllAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(\n                modelClass.getName())), values, conditions);\n    }\n\n    /**\n     * Updates all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL UPDATE statement and sends\n     * it to the database.\n     *\n     * <pre>\n     * ContentValues cv = new ContentValues();\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     * LitePal.update(&quot;person&quot;, cv, &quot;name = ?&quot;, &quot;Tom&quot;);\n     * </pre>\n     *\n     * This means that all the records which name is Tom will be updated into\n     * Jim.\n     *\n     * @param tableName\n     *            Which table to update.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            updating. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return The number of rows affected.\n     */\n    public static int updateAll(String tableName, ContentValues values,\n                                String... conditions) {\n        synchronized (LitePalSupport.class) {\n            UpdateHandler updateHandler = new UpdateHandler(Connector.getDatabase());\n            return updateHandler.onUpdateAll(tableName, values, conditions);\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static UpdateOrDeleteExecutor updateAllAsync(final String tableName, final ContentValues values, final String... conditions) {\n        final UpdateOrDeleteExecutor executor = new UpdateOrDeleteExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int rowsAffected = updateAll(tableName, values, conditions);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(rowsAffected);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Saves the collection into database. <br>\n     *\n     * <pre>\n     * LitePal.saveAll(people);\n     * </pre>\n     *\n     * If the model in collection is a new record gets created in the database,\n     * otherwise the existing record gets updated.<br>\n     * If saving process failed by any accident, the whole action will be\n     * cancelled and your database will be <b>rolled back</b>. <br>\n     * This method acts the same result as the below way, but <b>much more\n     * efficient</b>.\n     *\n     * <pre>\n     * for (Person person : people) {\n     * \tperson.save();\n     * }\n     * </pre>\n     *\n     * So when your collection holds huge of models, saveAll(Collection) is the better choice.\n     *\n     * @param collection\n     *            Holds all models to save.\n     * @return True if all records in collection are saved. False none record in collection is saved. There won't be partial saved condition.\n     */\n    public static <T extends LitePalSupport> boolean saveAll(Collection<T> collection) {\n        synchronized (LitePalSupport.class) {\n            SQLiteDatabase db = Connector.getDatabase();\n            db.beginTransaction();\n            try {\n                SaveHandler saveHandler = new SaveHandler(db);\n                saveHandler.onSaveAll(collection);\n                db.setTransactionSuccessful();\n                return true;\n            } catch (Exception e) {\n                e.printStackTrace();\n                return false;\n            } finally {\n                db.endTransaction();\n            }\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public static <T extends LitePalSupport> SaveExecutor saveAllAsync(final Collection<T> collection) {\n        final SaveExecutor executor = new SaveExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    boolean success;\n                    try {\n                        saveAll(collection);\n                        success = true;\n                    } catch (Exception e) {\n                        success = false;\n                    }\n                    final boolean result = success;\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(result);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n     * Provide a way to mark all models in collection as deleted. This means these models' save\n     * state is no longer exist anymore. If save them again, they will be treated as inserting new\n     * data instead of updating the exist one.\n     * @param collection\n     *          Collection of models which want to mark as deleted and clear their save state.\n     */\n    public static <T extends LitePalSupport> void markAsDeleted(Collection<T> collection) {\n        for (T t : collection) {\n            t.clearSavedState();\n        }\n    }\n\n    /**\n     * Check if the specified conditions data already exists in the table.\n     * @param modelClass\n     *          Which table to check by class.\n     * @param conditions\n     *          A filter declaring which data to check. Exactly same use as\n     *          {@link Operator#where(String...)}, except null conditions will result in false.\n     * @return Return true if the specified conditions data already exists in the table.\n     *         False otherwise. Null conditions will result in false.\n     */\n    public static <T> boolean isExist(Class<T> modelClass, String... conditions) {\n        return conditions != null && where(conditions).count(modelClass) > 0;\n    }\n\n    /**\n     * Register a listener to listen database create and upgrade events.\n     */\n    public static void registerDatabaseListener(DatabaseListener listener) {\n        dbListener = listener;\n    }\n\n    public static DatabaseListener getDBListener() {\n        return dbListener;\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/annotation/Column.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Used for adding constraints to a column. Note that this annotation won't affect id column.\n *\n * @author Tony Green\n * @since 1.3\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\npublic @interface Column {\n\n    /**\n     * Set nullable constraint for the column.\n     */\n    boolean nullable() default true;\n\n    /**\n     * Set unique constraint for the column.\n     */\n    boolean unique() default false;\n\n    /**\n     * Set default value with String type for the column regardless of what column type is.\n     */\n    String defaultValue() default \"\";\n\n    /**\n     * Ignore to map this field into a column.\n     */\n    boolean ignore() default false;\n\n    /**\n     * Add index for the column.\n     */\n    boolean index() default false;\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/annotation/Encrypt.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Used for encrypt string field value when persisted into database.\n *\n * @author Tony Green\n * @since 1.6\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\npublic @interface Encrypt {\n\n    /**\n     * Set the algorithm for encryption.\n     */\n    String algorithm();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/AssociationsAnalyzer.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\n\nimport org.litepal.LitePalBase;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.util.DBUtility;\n\n/**\n * Base class of associations analyzer.\n * \n * @author Tony Green\n * @since 1.1\n */\nabstract class AssociationsAnalyzer extends DataHandler {\n\n\t/**\n\t * Get the associated models collection of associated model. Used for\n\t * reverse searching associations.\n\t * \n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t * @param associationInfo\n\t *            To get reverse associated models collection.\n\t * @return The associated models collection of associated model by analyzing\n\t *         associationInfo.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprotected Collection<LitePalSupport> getReverseAssociatedModels(LitePalSupport associatedModel,\n                                                                    AssociationsInfo associationInfo) throws SecurityException, IllegalArgumentException,\n\t\t\tNoSuchMethodException, IllegalAccessException, InvocationTargetException {\n\t\treturn (Collection<LitePalSupport>) getFieldValue(associatedModel,\n\t\t\t\tassociationInfo.getAssociateSelfFromOtherModel());\n\t}\n\n\t/**\n\t * Set the associated models collection of associated model. Break quote of\n\t * source collection.\n\t * \n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t * @param associationInfo\n\t *            To get reverse associated models collection.\n\t * @param associatedModelCollection\n\t *            The new associated models collection with same data as source\n\t *            collection but different quote.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\tprotected void setReverseAssociatedModels(LitePalSupport associatedModel,\n\t\t\tAssociationsInfo associationInfo, Collection<LitePalSupport> associatedModelCollection)\n\t\t\tthrows SecurityException, IllegalArgumentException, NoSuchMethodException,\n\t\t\tIllegalAccessException, InvocationTargetException {\n\t\tsetFieldValue(associatedModel, associationInfo.getAssociateSelfFromOtherModel(),\n\t\t\t\tassociatedModelCollection);\n\t}\n\n\t/**\n\t * Check the associated model collection. If the associated model collection\n\t * is null, try to initialize the associated model collection by the given\n\t * associated field. If the associated field is subclass of List, make an\n\t * instance of ArrayList for associated model collection. If the associated\n\t * field is subclass of Set, make an instance of HashSet for associated\n\t * model collection. If the associated model collection is not null, doing\n\t * nothing.\n\t * \n\t * @param associatedModelCollection\n\t *            The associated model collection to check null and initialize.\n\t * @param associatedField\n\t *            The field to decide which type to initialize for associated\n\t *            model collection.\n\t * @throws LitePalSupportException\n\t */\n\tprotected Collection<LitePalSupport> checkAssociatedModelCollection(\n            Collection<LitePalSupport> associatedModelCollection, Field associatedField) {\n\t\tCollection<LitePalSupport> collection = null;\n\t\tif (isList(associatedField.getType())) {\n\t\t\tcollection = new ArrayList<LitePalSupport>();\n\t\t} else if (isSet(associatedField.getType())) {\n\t\t\tcollection = new HashSet<LitePalSupport>();\n\t\t} else {\n\t\t\tthrow new LitePalSupportException(LitePalSupportException.WRONG_FIELD_TYPE_FOR_ASSOCIATIONS);\n\t\t}\n\t\tif (associatedModelCollection != null) {\n\t\t\tcollection.addAll(associatedModelCollection);\n\t\t}\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Build the bidirectional association by setting the baseObj instance to\n\t * the associated model.\n\t * \n\t * @param baseObj\n\t *            The instance of self model.\n\t * @param associatedModel\n\t *            The associated model.\n\t * @param associationInfo\n\t *            The association info to get the association.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\tprotected void buildBidirectionalAssociations(LitePalSupport baseObj, LitePalSupport associatedModel,\n                                                  AssociationsInfo associationInfo) throws SecurityException, IllegalArgumentException,\n\t\t\tNoSuchMethodException, IllegalAccessException, InvocationTargetException {\n\t\tsetFieldValue(associatedModel, associationInfo.getAssociateSelfFromOtherModel(),\n\t\t\t\tbaseObj);\n\t}\n\n\t/**\n\t * If the associated model is saved, add its' name and id to baseObj by\n\t * calling {@link LitePalSupport#addAssociatedModelWithFK(String, long)}. Or if\n\t * the baseObj is saved, add its' name and id to associated model by calling\n\t * {@link LitePalSupport#addAssociatedModelWithoutFK(String, long)}.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist.\n\t * @param associatedModel\n\t *            The associated model.\n\t */\n\tprotected void dealsAssociationsOnTheSideWithoutFK(LitePalSupport baseObj,\n\t\t\tLitePalSupport associatedModel) {\n\t\tif (associatedModel != null) {\n\t\t\tif (associatedModel.isSaved()) {\n\t\t\t\tbaseObj.addAssociatedModelWithFK(associatedModel.getTableName(),\n\t\t\t\t\t\tassociatedModel.getBaseObjId());\n\t\t\t} else {\n\t\t\t\tif (baseObj.isSaved()) {\n\t\t\t\t\tassociatedModel.addAssociatedModelWithoutFK(baseObj.getTableName(),\n\t\t\t\t\t\t\tbaseObj.getBaseObjId());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * If the associated model of self model is null, the FK value in database\n\t * should be cleared if it exists when updating.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associationInfo\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t */\n\tprotected void mightClearFKValue(LitePalSupport baseObj, AssociationsInfo associationInfo) {\n\t\tbaseObj.addFKNameToClearSelf(getForeignKeyName(associationInfo));\n\t}\n\n\t/**\n\t * Get foreign key name by {@link org.litepal.crud.model.AssociationsInfo}.\n\t * \n\t * @param associationInfo\n\t *            To get foreign key name from.\n\t * @return The foreign key name.\n\t */\n\tprivate String getForeignKeyName(AssociationsInfo associationInfo) {\n\t\treturn getForeignKeyColumnName(DBUtility.getTableNameByClassName(associationInfo\n\t\t\t\t.getAssociatedClassName()));\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/DataHandler.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.util.Log;\nimport android.util.SparseArray;\n\nimport org.litepal.LitePalBase;\nimport org.litepal.Operator;\nimport org.litepal.annotation.Column;\nimport org.litepal.annotation.Encrypt;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.exceptions.DatabaseGenerateException;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.tablemanager.model.GenericModel;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.Const;\nimport org.litepal.util.DBUtility;\nimport org.litepal.util.cipher.CipherUtil;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.litepal.util.BaseUtility.changeCase;\n\n/**\n * This is the base class for CRUD component. All the common actions which can\n * be shared with each function in CURD component will be put here.\n * \n * @author Tony Green\n * @since 1.1\n */\nabstract class DataHandler extends LitePalBase {\n\tpublic static final String TAG = \"DataHandler\";\n\n\t/**\n\t * Instance of SQLiteDatabase, use to do the CRUD job.\n\t */\n\tSQLiteDatabase mDatabase;\n\n\t/**\n\t * Store empty model instance. In case to create each time when checking\n\t * field is with default value or not.\n\t */\n\tprivate LitePalSupport tempEmptyModel;\n\n\t/**\n\t * Holds the AssociationsInfo which foreign keys in the current model.\n\t */\n\tprivate List<AssociationsInfo> fkInCurrentModel;\n\n\t/**\n\t * Holds the AssociationsInfo which foreign keys in other models.\n\t */\n\tprivate List<AssociationsInfo> fkInOtherModel;\n\n\t/**\n\t * Query the table of the given model, returning a model list over the\n\t * result set.\n\t * \n\t * @param modelClass\n\t *            The model to compile the query against.\n\t * @param columns\n\t *            A list of which columns to return. Passing null will return\n\t *            all columns, which is discouraged to prevent reading data from\n\t *            storage that isn't going to be used.\n\t * @param selection\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause (excluding the WHERE itself). Passing null will\n\t *            return all rows for the given table.\n\t * @param selectionArgs\n\t *            You may include ?s in selection, which will be replaced by the\n\t *            values from selectionArgs, in order that they appear in the\n\t *            selection. The values will be bound as Strings.\n\t * @param groupBy\n\t *            A filter declaring how to group rows, formatted as an SQL\n\t *            GROUP BY clause (excluding the GROUP BY itself). Passing null\n\t *            will cause the rows to not be grouped.\n\t * @param having\n\t *            A filter declare which row groups to include in the cursor, if\n\t *            row grouping is being used, formatted as an SQL HAVING clause\n\t *            (excluding the HAVING itself). Passing null will cause all row\n\t *            groups to be included, and is required when row grouping is\n\t *            not being used.\n\t * @param orderBy\n\t *            How to order the rows, formatted as an SQL ORDER BY clause\n\t *            (excluding the ORDER BY itself). Passing null will use the\n\t *            default sort order, which may be unordered.\n\t * @param limit\n\t *            Limits the number of rows returned by the query, formatted as\n\t *            LIMIT clause. Passing null denotes no LIMIT clause.\n\t * @param foreignKeyAssociations\n\t *            Associated classes which have foreign keys in the current\n\t *            model's table.\n\t * @return A model list. The list may be empty.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprotected <T> List<T> query(Class<T> modelClass, String[] columns, String selection,\n\t\t\tString[] selectionArgs, String groupBy, String having, String orderBy, String limit,\n\t\t\tList<AssociationsInfo> foreignKeyAssociations) {\n\t\tList<T> dataList = new ArrayList<>();\n\t\tCursor cursor = null;\n\t\ttry {\n            List<Field> supportedFields = getSupportedFields(modelClass.getName());\n            List<Field> supportedGenericFields = getSupportedGenericFields(modelClass.getName());\n            String[] customizedColumns = DBUtility.convertSelectClauseToValidNames(getCustomizedColumns(columns, supportedGenericFields, foreignKeyAssociations));\n            String tableName = getTableName(modelClass);\n\t\t\tcursor = mDatabase.query(tableName, customizedColumns, selection, selectionArgs,\n\t\t\t\t\tgroupBy, having, orderBy, limit);\n\t\t\tif (cursor.moveToFirst()) {\n                SparseArray<QueryInfoCache> queryInfoCacheSparseArray = new SparseArray<>();\n                Map<Field, GenericModel> genericModelMap = new HashMap<>();\n\t\t\t\tdo {\n\t\t\t\t\tT modelInstance = (T) createInstanceFromClass(modelClass);\n\t\t\t\t\tgiveBaseObjIdValue((LitePalSupport) modelInstance,\n\t\t\t\t\t\t\tcursor.getLong(cursor.getColumnIndexOrThrow(\"id\")));\n\t\t\t\t\tsetValueToModel(modelInstance, supportedFields, foreignKeyAssociations, cursor, queryInfoCacheSparseArray);\n                    setGenericValueToModel((LitePalSupport) modelInstance, supportedGenericFields, genericModelMap);\n\t\t\t\t\tif (foreignKeyAssociations != null) {\n\t\t\t\t\t\tsetAssociatedModel((LitePalSupport) modelInstance);\n\t\t\t\t\t}\n\t\t\t\t\tdataList.add(modelInstance);\n\t\t\t\t} while (cursor.moveToNext());\n                queryInfoCacheSparseArray.clear();\n                genericModelMap.clear();\n\t\t\t}\n\t\t\treturn dataList;\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t} finally {\n\t\t\tif (cursor != null) {\n\t\t\t\tcursor.close();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Handles the math query of the given table.\n\t * \n\t * @param tableName\n\t *            Which table to query from.\n\t * @param columns\n\t *            A list of which columns to return. Passing null will return\n\t *            all columns, which is discouraged to prevent reading data from\n\t *            storage that isn't going to be used.\n\t * @param conditions\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause. Passing null will return all rows.\n\t * @param type\n\t *            The type of the based on column.\n\t * @return The result calculating by SQL.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprotected <T> T mathQuery(String tableName, String[] columns, String[] conditions, Class<T> type) {\n\t\tBaseUtility.checkConditionsCorrect(conditions);\n\t\tCursor cursor = null;\n\t\tT result = null;\n\t\ttry {\n\t\t\tcursor = mDatabase.query(tableName, columns, getWhereClause(conditions),\n\t\t\t\t\tgetWhereArgs(conditions), null, null, null);\n\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\tClass<?> cursorClass = cursor.getClass();\n\t\t\t\tMethod method = cursorClass.getMethod(genGetColumnMethod(type), int.class);\n\t\t\t\tresult = (T) method.invoke(cursor, 0);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t} finally {\n\t\t\tif (cursor != null) {\n\t\t\t\tcursor.close();\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Assign the generated id value to {@link LitePalSupport#baseObjId}. This\n\t * value will be used as identify of this model for system use.\n\t * \n\t * @param baseObj\n\t *            The class of base object.\n\t * @param id\n\t *            The value of id.\n\t */\n\tprotected void giveBaseObjIdValue(LitePalSupport baseObj, long id) throws SecurityException,\n\t\t\tNoSuchFieldException, IllegalArgumentException, IllegalAccessException {\n\t\tif (id > 0) {\n\t\t\tDynamicExecutor.set(baseObj, \"baseObjId\", id, LitePalSupport.class);\n\t\t}\n\t}\n\n\t/**\n\t * Iterate all the fields passed in. Each field calls\n\t * {@link #putFieldsValueDependsOnSaveOrUpdate(LitePalSupport, java.lang.reflect.Field, android.content.ContentValues)}\n\t * if it's not id field.\n\t * \n\t * @param baseObj\n\t *            Current model to persist or update.\n\t * @param supportedFields\n\t *            List of all supported fields.\n\t * @param values\n\t *            To store data of current model for persisting or updating.\n\t */\n\tprotected void putFieldsValue(LitePalSupport baseObj, List<Field> supportedFields,\n                                  ContentValues values) throws SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {\n\t\tfor (Field field : supportedFields) {\n\t\t\tif (!isIdColumn(field.getName())) {\n\t\t\t\tputFieldsValueDependsOnSaveOrUpdate(baseObj, field, values);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * This method deals with the putting values job into ContentValues. The\n\t * ContentValues has <b>put</b> method to set data. But we do not know we\n\t * should use which <b>put</b> method cause the field type isn't clear. So\n\t * the reflection API is necessary here to put values into ContentValues\n\t * with dynamically getting field type to put value.\n\t * \n\t * @param baseObj\n\t *            The class of base object.\n\t * @param field\n\t *            Field to put into ContentValues.\n\t * @param values\n\t *            To store data of current model for persisting or updating.\n\t */\n\tprotected void putContentValuesForSave(LitePalSupport baseObj, Field field, ContentValues values)\n\t\t\tthrows SecurityException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {\n        Object fieldValue = getFieldValue(baseObj, field);\n        if (\"java.util.Date\".equals(field.getType().getName())) {\n        \t// handle java.util.Date type for special\n\t\t\tif (fieldValue != null) {\n\t\t\t\t// If Date field is not null, use date.getTime() value for save.\n\t\t\t\tDate date = (Date) fieldValue;\n\t\t\t\tfieldValue = date.getTime();\n\t\t\t} else {\n\t\t\t\t// If Date field is null, try to use defaultValue on annotation first.\n\t\t\t\tColumn annotation = field.getAnnotation(Column.class);\n\t\t\t\tif (annotation != null) {\n\t\t\t\t\tString defaultValue = annotation.defaultValue();\n\t\t\t\t\tif (!defaultValue.isEmpty()) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfieldValue = Long.parseLong(defaultValue);\n\t\t\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\t\t\tLog.w(TAG, field + \" in \" + baseObj.getClass() + \" with invalid defaultValue. So we use null instead\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (fieldValue == null) {\n\t\t\t\t\t// If Date field is still null, use Long.MAX_VALUE for save. Because it's a date that will never reach.\n\t\t\t\t\tfieldValue = Long.MAX_VALUE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (fieldValue != null) {\n\t\t\t// put content value only when value is not null. this allows to use defaultValue declared in annotation.\n\t\t\tEncrypt annotation = field.getAnnotation(Encrypt.class);\n\t\t\tif (annotation != null && \"java.lang.String\".equals(field.getType().getName())) {\n\t\t\t\tfieldValue = encryptValue(annotation.algorithm(), fieldValue);\n\t\t\t}\n\t\t\tObject[] parameters = new Object[] { changeCase(DBUtility.convertToValidColumnName(field.getName())), fieldValue };\n\t\t\tClass<?>[] parameterTypes = getParameterTypes(field, fieldValue, parameters);\n\t\t\tDynamicExecutor.send(values, \"put\", parameters, values.getClass(), parameterTypes);\n\t\t}\n\t}\n\n    /**\n     * putContentValuesForUpdate operation is almost same with putContentValuesForSave, except allowing put null fieldValue into database,\n     * which is made for {@link LitePalSupport#setToDefault} function.\n     *\n     * @param baseObj\n     *            The class of base object.\n     * @param field\n     *            Field to put into ContentValues.\n     * @param values\n     *            To store data of current model for persisting or updating.\n     */\n    protected void putContentValuesForUpdate(LitePalSupport baseObj, Field field, ContentValues values)\n            throws SecurityException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {\n        Object fieldValue = getFieldValue(baseObj, field);\n        if (\"java.util.Date\".equals(field.getType().getName())) {\n        \tif (fieldValue != null) {\n\t\t\t\tDate date = (Date) fieldValue;\n\t\t\t\tfieldValue = date.getTime();\n\t\t\t} else {\n\t\t\t\t// If Date field is null, use Long.MAX_VALUE for save. Because it's a date that will never reach.\n        \t\tfieldValue = Long.MAX_VALUE;\n\t\t\t}\n        }\n        Encrypt annotation = field.getAnnotation(Encrypt.class);\n        if (annotation != null && \"java.lang.String\".equals(field.getType().getName())) {\n            fieldValue = encryptValue(annotation.algorithm(), fieldValue);\n        }\n        Object[] parameters = new Object[] { changeCase(DBUtility.convertToValidColumnName(field.getName())), fieldValue };\n        Class<?>[] parameterTypes = getParameterTypes(field, fieldValue, parameters);\n        DynamicExecutor.send(values, \"put\", parameters, values.getClass(), parameterTypes);\n    }\n\n    /**\n     * Encrypt the field value with targeted algorithm.\n     * @param algorithm\n     *          The algorithm to encrypt value.\n     * @param fieldValue\n     *          Field value to encrypt.\n     * @return Encrypted value by targeted algorithm.\n     */\n    protected Object encryptValue(String algorithm, Object fieldValue) {\n        if (algorithm != null && fieldValue != null) {\n            if (LitePalSupport.AES.equalsIgnoreCase(algorithm)) {\n                fieldValue = CipherUtil.aesEncrypt((String) fieldValue);\n            } else if (LitePalSupport.MD5.equalsIgnoreCase(algorithm)) {\n                fieldValue = CipherUtil.md5Encrypt((String) fieldValue);\n            }\n        }\n        return fieldValue;\n    }\n\n\t/**\n\t * Get the field value for model.\n\t * \n\t * @param dataSupport\n\t *            The model to get method from.\n\t * @param field\n\t *            Use to generate getter method name.\n\t * @return The value returned by getter method.\n\t */\n\tprotected Object getFieldValue(LitePalSupport dataSupport, Field field)\n\t\t\tthrows SecurityException, IllegalArgumentException,\n\t\t\tIllegalAccessException {\n\t\tif (shouldGetOrSet(dataSupport, field)) {\n\t\t\treturn DynamicExecutor.getField(dataSupport, field.getName(), dataSupport.getClass());\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Set the field value for model.\n\t * \n\t * @param dataSupport\n\t *            The model to set method to.\n\t * @param field\n\t *            Use to generate setter method name.\n\t * @param parameter\n\t *            The parameter to invoke setter method.\n\t */\n\tprotected void setFieldValue(LitePalSupport dataSupport, Field field, Object parameter)\n\t\t\tthrows SecurityException, IllegalArgumentException, IllegalAccessException {\n\t\tif (shouldGetOrSet(dataSupport, field)) {\n            DynamicExecutor.setField(dataSupport, field.getName(), parameter, dataSupport.getClass());\n\t\t}\n\t}\n\n\t/**\n\t * Find all the associated models of currently model. Then add all the\n\t * associated models into baseObj.\n\t * \n\t * @param baseObj\n\t *            The class of base object.\n\t */\n\tprotected void analyzeAssociatedModels(LitePalSupport baseObj, Collection<AssociationsInfo> associationInfos) {\n\t\ttry {\n\t\t\tfor (AssociationsInfo associationInfo : associationInfos) {\n\t\t\t\tif (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE) {\n\t\t\t\t\tnew Many2OneAnalyzer().analyze(baseObj, associationInfo);\n\t\t\t\t} else if (associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {\n\t\t\t\t\tnew One2OneAnalyzer().analyze(baseObj, associationInfo);\n\t\t\t\t} else if (associationInfo.getAssociationType() == Const.Model.MANY_TO_MANY) {\n\t\t\t\t\tnew Many2ManyAnalyzer().analyze(baseObj, associationInfo);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t}\n\t}\n\t\n\t/**\n\t * Get the associated model.\n\t * \n\t * @param baseObj\n\t *            The instance of self model.\n\t * @param associationInfo\n\t *            To get the associated model.\n\t * @return The associated model of self model by analyzing associationInfo.\n\t */\n\tprotected LitePalSupport getAssociatedModel(LitePalSupport baseObj, AssociationsInfo associationInfo)\n\t\t\tthrows SecurityException, IllegalArgumentException, IllegalAccessException {\n\t\treturn (LitePalSupport) getFieldValue(baseObj,\n\t\t\t\tassociationInfo.getAssociateOtherModelFromSelf());\n\t}\n\n\t/**\n\t * Get the associated models collection. When it comes to many2one or\n\t * many2many association. A model may have lots of associated models.\n\t * \n\t * @param baseObj\n\t *            The instance of self model.\n\t * @param associationInfo\n\t *            To get the associated models collection.\n\t * @return The associated models collection of self model by analyzing\n\t *         associationInfo.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprotected Collection<LitePalSupport> getAssociatedModels(LitePalSupport baseObj, AssociationsInfo associationInfo) throws SecurityException, IllegalArgumentException, IllegalAccessException {\n\t\treturn (Collection<LitePalSupport>) getFieldValue(baseObj,\n\t\t\t\tassociationInfo.getAssociateOtherModelFromSelf());\n\t}\n\n\t/**\n\t * Create an empty instance of baseObj if it hasn't created one yet. If\n\t * there's already an empty model existed in {@link #tempEmptyModel}, no\n\t * need to create a new one.\n\t * \n\t * @param baseObj\n\t *            Current model to update.\n\t * @return An empty instance of baseObj.\n\t */\n\tprotected LitePalSupport getEmptyModel(LitePalSupport baseObj) {\n\t\tif (tempEmptyModel != null) {\n\t\t\treturn tempEmptyModel;\n\t\t}\n\t\tString className = null;\n\t\ttry {\n\t\t\tclassName = baseObj.getClassName();\n\t\t\tClass<?> modelClass = Class.forName(className);\n\t\t\ttempEmptyModel = (LitePalSupport) modelClass.newInstance();\n\t\t\treturn tempEmptyModel;\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tthrow new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND\n\t\t\t\t\t+ className);\n\t\t} catch (InstantiationException e) {\n\t\t\tthrow new LitePalSupportException(className + LitePalSupportException.INSTANTIATION_EXCEPTION, e);\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Get the WHERE clause to apply when updating or deleting multiple rows.\n\t * \n\t * @param conditions\n\t *            A string array representing the WHERE part of an SQL\n\t *            statement.\n\t * @return The WHERE clause to apply when updating or deleting multiple\n\t *         rows.\n\t */\n\tprotected String getWhereClause(String... conditions) {\n\t\tif (isAffectAllLines((Object) conditions)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (conditions != null && conditions.length > 0) {\n\t\t\treturn conditions[0];\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get the WHERE arguments to fill into where clause when updating or\n\t * deleting multiple rows.\n\t * \n\t * @param conditions\n\t *            A string array representing the WHERE part of an SQL\n\t *            statement.\n\t * @return The WHERE arguments to fill into where clause when updating or\n\t *         deleting multiple rows.\n\t */\n\tprotected String[] getWhereArgs(String... conditions) {\n\t\tif (isAffectAllLines((Object) conditions)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (conditions != null && conditions.length > 1) {\n\t\t\tString[] whereArgs = new String[conditions.length - 1];\n\t\t\tSystem.arraycopy(conditions, 1, whereArgs, 0, conditions.length - 1);\n\t\t\treturn whereArgs;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Check the passing conditions represent to affect all lines or not. <br>\n\t * Do not pass anything to the conditions parameter means affect all lines.\n\t * \n\t * @param conditions\n\t *            An array representing the WHERE part of an SQL statement.\n\t * @return Affect all lines or not.\n\t */\n\tprotected boolean isAffectAllLines(Object... conditions) {\n\t\treturn conditions != null && conditions.length == 0;\n\t}\n\n\t/**\n\t * Get the where clause by the passed in id collection to apply multiple\n\t * rows.\n\t * \n\t * @param ids\n\t *            The id collection.\n\t * @return The where clause to execute.\n\t */\n\tprotected String getWhereOfIdsWithOr(Collection<Long> ids) {\n\t\tStringBuilder whereClause = new StringBuilder();\n\t\tboolean needOr = false;\n\t\tfor (long id : ids) {\n\t\t\tif (needOr) {\n\t\t\t\twhereClause.append(\" or \");\n\t\t\t}\n\t\t\tneedOr = true;\n\t\t\twhereClause.append(\"id = \");\n\t\t\twhereClause.append(id);\n\t\t}\n\t\treturn changeCase(whereClause.toString());\n\t}\n\n\t/**\n\t * Get the where clause by the passed in id array to apply multiple rows.\n\t * \n\t * @param ids\n\t *            The id collection.\n\t * @return The where clause to execute.\n\t */\n\tprotected String getWhereOfIdsWithOr(long... ids) {\n\t\tStringBuilder whereClause = new StringBuilder();\n\t\tboolean needOr = false;\n\t\tfor (long id : ids) {\n\t\t\tif (needOr) {\n\t\t\t\twhereClause.append(\" or \");\n\t\t\t}\n\t\t\tneedOr = true;\n\t\t\twhereClause.append(\"id = \");\n\t\t\twhereClause.append(id);\n\t\t}\n\t\treturn changeCase(whereClause.toString());\n\t}\n\n\t/**\n\t * When executing {@link #getFieldValue(LitePalSupport, Field)} or\n\t * {@link #setFieldValue(LitePalSupport, Field, Object)}, the\n\t * dataSupport and field passed in should be protected from null value.\n\t * \n\t * @param dataSupport\n\t *            The object to execute set or get method.\n\t * @param field\n\t *            The field of generating set and get methods.\n\t * @return True if dataSupport and field are not null, false otherwise.\n\t */\n\tprotected boolean shouldGetOrSet(LitePalSupport dataSupport, Field field) {\n\t\treturn dataSupport != null && field != null;\n\t}\n\n\t/**\n\t * Get the name of intermediate join table.\n\t * \n\t * @param baseObj\n\t *            Current model.\n\t * @param associatedTableName\n\t *            The name of associated table.\n\t * @return The name of intermediate join table.\n\t */\n\tprotected String getIntermediateTableName(LitePalSupport baseObj, String associatedTableName) {\n\t\treturn changeCase(DBUtility.getIntermediateTableName(baseObj.getTableName(),\n                associatedTableName));\n\t}\n\n\t/**\n\t * Get the simple name of modelClass. Then change the case by the setting\n\t * rule in litepal.xml as table name.\n\t * \n\t * @param modelClass\n\t *            Class of model to get table name from.\n\t * @return The table name of model.\n\t */\n\tprotected String getTableName(Class<?> modelClass) {\n\t\treturn BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName()));\n\t}\n\t\n\t/**\n\t * Creates an instance from the passed in class. It will always create an\n\t * instance no matter how the constructor defines in the class file. A best\n\t * suit constructor will be find by calling\n\t * {@link #findBestSuitConstructor(Class)} method.\n\t * \n\t * @param modelClass\n\t *            The class to create instance.\n\t * @return An instance by the passed in class.\n\t */\n\tprotected Object createInstanceFromClass(Class<?> modelClass) {\n\t\ttry {\n\t\t\tConstructor<?> constructor = findBestSuitConstructor(modelClass);\n\t\t\treturn constructor.newInstance(getConstructorParams(modelClass, constructor));\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Finds the best suit constructor for creating an instance of a class. The\n\t * principle is that the constructor with least parameters and has no self\n\t * type parameter will be the best suit one to create instance.\n\t * \n\t * @param modelClass\n\t *            To get constructors from.\n\t * @return The best suit constructor.\n\t */\n\tprotected Constructor<?> findBestSuitConstructor(Class<?> modelClass) {\n\t\tConstructor<?>[] constructors = modelClass.getDeclaredConstructors();\n\t\tif (constructors.length == 0) throw new LitePalSupportException( modelClass.getName() + \" has no constructor. LitePal could not handle it\");\n\t\tConstructor<?> bestSuitConstructor = null;\n\t\tint minConstructorParamLength = Integer.MAX_VALUE;\n\t\tfor (Constructor<?> constructor : constructors) {\n\t\t\tClass<?>[] types = constructor.getParameterTypes();\n\t\t\tboolean canUseThisConstructor = true; // under some conditions, constructor can not use for create instance\n\t\t\tfor (Class<?> parameterType : types) {\n\t\t\t\tif (parameterType == modelClass\n\t\t\t\t\t|| parameterType.getName().startsWith(\"com.android\") && parameterType.getName().endsWith(\"InstantReloadException\")) {\n\t\t\t\t\t// we can not use this constructor\n\t\t\t\t\tcanUseThisConstructor = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (canUseThisConstructor) { // we can use this constructor\n\t\t\t\tif (types.length < minConstructorParamLength) { // find the constructor with least parameter\n\t\t\t\t\tbestSuitConstructor = constructor;\n\t\t\t\t\tminConstructorParamLength = types.length;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (bestSuitConstructor != null) {\n\t\t\tbestSuitConstructor.setAccessible(true);\n\t\t} else {\n\t\t\tStringBuilder builder = new StringBuilder(modelClass.getName()).append(\" has no suited constructor to new instance. Constructors defined in class:\");\n\t\t\tfor (Constructor<?> constructor : constructors) {\n\t\t\t\tbuilder.append(\"\\n\").append(constructor.toString());\n\t\t\t}\n\t\t\tthrow new LitePalSupportException(builder.toString());\n\t\t}\n\t\treturn bestSuitConstructor;\n\t}\n\n\t/**\n\t * Depends on the passed in constructor, creating a parameters array with\n\t * initialized values for the constructor.\n\t * \n\t * @param modelClass\n\t *            The original class the this constructor belongs to.\n\t * @param constructor\n\t *            The constructor to get parameters for it.\n\t * \n\t * @return A parameters array with initialized values.\n\t */\n\tprotected Object[] getConstructorParams(Class<?> modelClass, Constructor<?> constructor) {\n\t\tClass<?>[] paramTypes = constructor.getParameterTypes();\n\t\tObject[] params = new Object[paramTypes.length];\n\t\tfor (int i = 0; i < paramTypes.length; i++) {\n\t\t\tparams[i] = getInitParamValue(modelClass, paramTypes[i]);\n\t\t}\n\t\treturn params;\n\t}\n\n\t/**\n\t * Get value from database by cursor, then set the value into modelInstance.\n\t * \n\t * @param modelInstance\n\t *            The model to set into.\n\t * @param supportedFields\n\t *            Corresponding to each column in database.\n\t * @param foreignKeyAssociations\n\t *            Associated classes which have foreign keys in the current\n\t *            model's table.\n\t * @param cursor\n\t *            Use to get value from database.\n     * @param sparseArray\n     *            Use SparseArray to cache the query information at first loop. Then the rest loop\n     *            can get query information directly to speed up.\n\t */\n\tprotected void setValueToModel(Object modelInstance, List<Field> supportedFields,\n\t\t\tList<AssociationsInfo> foreignKeyAssociations, Cursor cursor, SparseArray<QueryInfoCache> sparseArray) throws SecurityException,\n\t\t\tIllegalArgumentException, NoSuchMethodException, IllegalAccessException,\n\t\t\tInvocationTargetException {\n        int cacheSize = sparseArray.size();\n        if (cacheSize > 0) {\n            for (int i = 0; i < cacheSize; i++) {\n                int columnIndex = sparseArray.keyAt(i);\n                QueryInfoCache cache = sparseArray.get(columnIndex);\n                setToModelByReflection(modelInstance, cache.field, columnIndex, cache.getMethodName, cursor);\n            }\n        } else {\n            for (Field field : supportedFields) {\n                String getMethodName = genGetColumnMethod(field);\n                String columnName = isIdColumn(field.getName()) ? \"id\" : DBUtility.convertToValidColumnName(field.getName());\n                int columnIndex = cursor.getColumnIndex(BaseUtility.changeCase(columnName));\n                if (columnIndex != -1) {\n                    setToModelByReflection(modelInstance, field, columnIndex, getMethodName, cursor);\n                    QueryInfoCache cache = new QueryInfoCache();\n                    cache.getMethodName = getMethodName;\n                    cache.field = field;\n                    sparseArray.put(columnIndex, cache);\n                }\n            }\n        }\n\n\t\tif (foreignKeyAssociations != null) {\n\t\t\tfor (AssociationsInfo associationInfo : foreignKeyAssociations) {\n\t\t\t\tString foreignKeyColumn = getForeignKeyColumnName(DBUtility\n\t\t\t\t\t\t.getTableNameByClassName(associationInfo.getAssociatedClassName()));\n\t\t\t\tint columnIndex = cursor.getColumnIndex(foreignKeyColumn);\n\t\t\t\tif (columnIndex != -1) {\n\t\t\t\t\tlong associatedClassId = cursor.getLong(columnIndex);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tLitePalSupport associatedObj = (LitePalSupport) Operator.find(\n\t\t\t\t\t\t\t\tClass.forName(associationInfo.getAssociatedClassName()),\n\t\t\t\t\t\t\t\tassociatedClassId);\n\t\t\t\t\t\tif (associatedObj != null) {\n\t\t\t\t\t\t\tsetFieldValue((LitePalSupport) modelInstance,\n\t\t\t\t\t\t\t\t\tassociationInfo.getAssociateOtherModelFromSelf(), associatedObj);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n    /**\n     * Get generic value from generic tables, then set the value into the baseObj.\n     * @param baseObj\n     *          The model to set into.\n     * @param supportedGenericFields\n     *          List of all supported generic fields.\n     * @param genericModelMap\n     *          Use HashMap to cache the query information at first loop. Then the rest loop can\n     *          get query information directly to speed up.\n     */\n    protected void setGenericValueToModel(LitePalSupport baseObj, List<Field> supportedGenericFields,\n                                          Map<Field, GenericModel> genericModelMap) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {\n        for (Field field : supportedGenericFields) {\n            String tableName, genericValueColumnName, genericValueIdColumnName, getMethodName;\n            Cursor cursor = null;\n            GenericModel genericModel = genericModelMap.get(field);\n            if (genericModel == null) {\n                String genericTypeName = getGenericTypeName(field);\n                if (baseObj.getClassName().equals(genericTypeName)) {\n                    genericValueColumnName = DBUtility.getM2MSelfRefColumnName(field);\n                    getMethodName = \"getLong\";\n                } else {\n                    genericValueColumnName = DBUtility.convertToValidColumnName(field.getName());\n                    getMethodName = genGetColumnMethod(field);\n                }\n                tableName = DBUtility.getGenericTableName(baseObj.getClassName(), field.getName());\n                genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(baseObj.getClassName());\n                GenericModel model = new GenericModel();\n                model.setTableName(tableName);\n                model.setValueColumnName(genericValueColumnName);\n                model.setValueIdColumnName(genericValueIdColumnName);\n                model.setGetMethodName(getMethodName);\n                genericModelMap.put(field, model);\n            } else {\n                tableName = genericModel.getTableName();\n                genericValueColumnName = genericModel.getValueColumnName();\n                genericValueIdColumnName = genericModel.getValueIdColumnName();\n                getMethodName = genericModel.getGetMethodName();\n            }\n            try {\n                cursor = mDatabase.query(tableName, null, genericValueIdColumnName + \" = ?\",\n                        new String[]{ String.valueOf(baseObj.getBaseObjId()) }, null, null, null);\n                if (cursor.moveToFirst()) {\n                    do {\n                        int columnIndex = cursor.getColumnIndex(BaseUtility.changeCase(genericValueColumnName));\n                        if (columnIndex != -1) {\n                            setToModelByReflection(baseObj, field, columnIndex, getMethodName, cursor);\n                        }\n                    } while (cursor.moveToNext());\n                }\n            } finally {\n                if (cursor != null) {\n                    cursor.close();\n                }\n            }\n        }\n    }\n\n\t/**\n\t * Get the foreign key associations of the specified class.\n\t * \n\t * @param className\n\t *            The full class name.\n\t * @param isEager\n\t *            True to load the associated models, false not.\n\t * @return The foreign key associations of the specified class\n\t */\n\tprotected List<AssociationsInfo> getForeignKeyAssociations(String className, boolean isEager) {\n\t\tif (isEager) {\n\t\t\tanalyzeAssociations(className);\n\t\t\treturn fkInCurrentModel;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get the types of parameters for {@link android.content.ContentValues#put}. Need two\n\t * parameters. First is String type for key. Second is depend on field for\n\t * value.\n\t * \n\t * @param field\n\t *            The field to get parameter type.\n\t * @param fieldValue\n\t *            Value of the field. Only used to convert to String when the\n\t *            field is char.\n\t * @param parameters\n\t *            If the field is char, convert the value to String at index 1.\n\t * @return The types of parameters for {@link android.content.ContentValues#put}.\n\t */\n\tprotected Class<?>[] getParameterTypes(Field field, Object fieldValue, Object[] parameters) {\n\t\tClass<?>[] parameterTypes;\n\t\tif (isCharType(field)) {\n\t\t\tparameters[1] = String.valueOf(fieldValue);\n\t\t\tparameterTypes = new Class[] { String.class, String.class };\n\t\t} else {\n\t\t\tif (field.getType().isPrimitive()) {\n\t\t\t\tparameterTypes = new Class[] { String.class, getObjectType(field.getType()) };\n\t\t\t} else if (\"java.util.Date\".equals(field.getType().getName())) {\n\t\t\t\tparameterTypes = new Class[] { String.class, Long.class };\n\t\t\t} else {\n\t\t\t\tparameterTypes = new Class[] { String.class, field.getType() };\n\t\t\t}\n\t\t}\n\t\treturn parameterTypes;\n\t}\n\n\t/**\n\t * Each primitive type has a corresponding object type. For example int and\n\t * Integer, boolean and Boolean. This method gives a way to turn primitive\n\t * type into object type.\n\t * \n\t * @param primitiveType\n\t *            The class of primitive type.\n\t * @return If the passed in parameter is primitive type, return a\n\t *         corresponding object type. Otherwise return null.\n\t */\n\tprivate Class<?> getObjectType(Class<?> primitiveType) {\n\t\tif (primitiveType != null) {\n\t\t\tif (primitiveType.isPrimitive()) {\n\t\t\t\tString basicTypeName = primitiveType.getName();\n\t\t\t\tswitch (basicTypeName) {\n\t\t\t\t\tcase \"int\":\n\t\t\t\t\t\treturn Integer.class;\n\t\t\t\t\tcase \"short\":\n\t\t\t\t\t\treturn Short.class;\n\t\t\t\t\tcase \"long\":\n\t\t\t\t\t\treturn Long.class;\n\t\t\t\t\tcase \"float\":\n\t\t\t\t\t\treturn Float.class;\n\t\t\t\t\tcase \"double\":\n\t\t\t\t\t\treturn Double.class;\n\t\t\t\t\tcase \"boolean\":\n\t\t\t\t\t\treturn Boolean.class;\n\t\t\t\t\tcase \"char\":\n\t\t\t\t\t\treturn Character.class;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Gives the passed in parameter an initialized value. If the parameter is\n\t * basic data type or the corresponding object data type, return the default\n\t * data. Or return null.\n\t * \n\t * @param modelClass\n\t *            The original class the this constructor belongs to.\n\t * @param paramType\n\t *            Parameter to get initialized value.\n\t * @return Default data of basic data type or null.\n\t */\n\tprivate Object getInitParamValue(Class<?> modelClass, Class<?> paramType) {\n\t\tString paramTypeName = paramType.getName();\n\t\tif (\"boolean\".equals(paramTypeName) || \"java.lang.Boolean\".equals(paramTypeName)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (\"float\".equals(paramTypeName) || \"java.lang.Float\".equals(paramTypeName)) {\n\t\t\treturn 0f;\n\t\t}\n\t\tif (\"double\".equals(paramTypeName) || \"java.lang.Double\".equals(paramTypeName)) {\n\t\t\treturn 0.0;\n\t\t}\n\t\tif (\"int\".equals(paramTypeName) || \"java.lang.Integer\".equals(paramTypeName)) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (\"long\".equals(paramTypeName) || \"java.lang.Long\".equals(paramTypeName)) {\n\t\t\treturn 0L;\n\t\t}\n\t\tif (\"short\".equals(paramTypeName) || \"java.lang.Short\".equals(paramTypeName)) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (\"char\".equals(paramTypeName) || \"java.lang.Character\".equals(paramTypeName)) {\n\t\t\treturn ' ';\n\t\t}\n        if (\"[B\".equals(paramTypeName) || \"[Ljava.lang.Byte;\".equals(paramTypeName)) {\n            return new byte[0];\n        }\n\t\tif (\"java.lang.String\".equals(paramTypeName)) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (modelClass == paramType) {\n\t\t\treturn null;\n\t\t}\n\t\treturn createInstanceFromClass(paramType);\n\t}\n\n\t/**\n\t * Judge if the field is char or Character type.\n\t * \n\t * @param field\n\t *            Field to judge type.\n\t * @return Return true if it's char or Character. Otherwise return false.\n\t */\n\tprivate boolean isCharType(Field field) {\n\t\tString type = field.getType().getName();\n\t\treturn type.equals(\"char\") || type.endsWith(\"Character\");\n\t}\n\n\t/**\n\t * Judge a field is a primitive boolean type or not. Cause it's a little\n\t * special when use IDE to generate getter and setter method. The primitive\n\t * boolean type won't be like <b>getXxx</b>, it's something like\n\t * <b>isXxx</b>.\n\t * \n\t * @param field\n\t *            Use field to get field type.\n\t * @return If it's primitive boolean type return true, else return false.\n\t */\n\tprivate boolean isPrimitiveBooleanType(Field field) {\n\t\tClass<?> fieldType = field.getType();\n\t\treturn \"boolean\".equals(fieldType.getName());\n\t}\n\n\t/**\n\t * Put the value of field into ContentValues if current action is saving.\n\t * Check the value of field is default value or not if current action is\n\t * updating. If it's not default value, put it into ContentValues. Otherwise\n\t * ignore it.\n\t * \n\t * @param baseObj\n\t *            Current model to persist or update.\n\t * @param field\n\t *            With value to put into ContentValues.\n\t * @param values\n\t *            To store data of current model for persisting or updating.\n\t */\n\tprivate void putFieldsValueDependsOnSaveOrUpdate(LitePalSupport baseObj, Field field, ContentValues values)\n\t\t\tthrows SecurityException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {\n\t\tif (isUpdating()) {\n\t\t\tif (!isFieldWithDefaultValue(baseObj, field)) {\n\t\t\t\tputContentValuesForUpdate(baseObj, field, values);\n\t\t\t}\n\t\t} else if (isSaving()) {\n            putContentValuesForSave(baseObj, field, values);\n\t\t}\n\t}\n\n\t/**\n\t * Current action is updating or not. Note that update the record by saving\n\t * the already saved record again belongs to save action.\n\t * \n\t * @return If current action is updating return true. Otherwise return\n\t *         false.\n\t */\n\tprivate boolean isUpdating() {\n\t\treturn UpdateHandler.class.getName().equals(getClass().getName());\n\t}\n\n\t/**\n\t * Current action is saving or not. Note that update the record by saving\n\t * the already saved record again belongs to save action.\n\t * \n\t * @return If current action is saving return true. Otherwise return false.\n\t */\n\tprivate boolean isSaving() {\n\t\treturn SaveHandler.class.getName().equals(getClass().getName());\n\t}\n\n\t/**\n\t * Analyze the passed in field. Check if this field is with default value.\n\t * The baseObj need a default constructor or {@link LitePalSupportException}\n\t * will be thrown.\n\t * \n\t * @param baseObj\n\t *            Current model to update.\n\t * @param field\n\t *            To check if with default value.\n\t * @return If the field is with default value, return true. Otherwise return\n\t *         false.\n\t */\n\tprivate boolean isFieldWithDefaultValue(LitePalSupport baseObj, Field field)\n\t\t\tthrows IllegalAccessException, SecurityException, IllegalArgumentException {\n\t\tLitePalSupport emptyModel = getEmptyModel(baseObj);\n\t\tObject realReturn = getFieldValue(baseObj, field);\n\t\tObject defaultReturn = getFieldValue(emptyModel, field);\n\t\tif (realReturn != null && defaultReturn != null) {\n\t\t\tString realFieldValue = realReturn.toString();\n\t\t\tString defaultFieldValue = defaultReturn.toString();\n\t\t\treturn realFieldValue.equals(defaultFieldValue);\n\t\t}\n\t\treturn realReturn == defaultReturn;\n\t}\n\n\t/**\n\t * Generate the getter method name by field, following the Android Studio rule.\n\t * \n\t * @param field\n\t *            The field to generate getter method from.\n\t * @return The generated getter method name.\n\t */\n\tprotected String makeGetterMethodName(Field field) {\n\t\tString getterMethodPrefix;\n\t\tString fieldName = field.getName();\n\t\tif (isPrimitiveBooleanType(field)) {\n\t\t\tif (fieldName.matches(\"^is[A-Z]{1}.*$\")) {\n\t\t\t\tfieldName = fieldName.substring(2);\n\t\t\t}\n\t\t\tgetterMethodPrefix = \"is\";\n\t\t} else {\n\t\t\tgetterMethodPrefix = \"get\";\n\t\t}\n\t\tif (fieldName.matches(\"^[a-z]{1}[A-Z]{1}.*\")) {\n\t\t\treturn getterMethodPrefix + fieldName;\n\t\t} else {\n\t\t\treturn getterMethodPrefix + BaseUtility.capitalize(fieldName);\n\t\t}\n\t}\n\n\t/**\n\t * Generate the setter method name by field, following the Android Studio rule.\n\t * \n\t * @param field\n\t *            The field to generate setter method from.\n\t * @return The generated setter method name.\n\t */\n\tprotected String makeSetterMethodName(Field field) {\n\t\tString setterMethodName;\n\t\tString setterMethodPrefix = \"set\";\n\t\tif (isPrimitiveBooleanType(field) && field.getName().matches(\"^is[A-Z]{1}.*$\")) {\n\t\t\tsetterMethodName = setterMethodPrefix + field.getName().substring(2);\n\t\t} else if (field.getName().matches(\"^[a-z]{1}[A-Z]{1}.*\")) {\n\t\t\tsetterMethodName = setterMethodPrefix + field.getName();\n\t\t} else {\n\t\t\tsetterMethodName = setterMethodPrefix + BaseUtility.capitalize(field.getName());\n\t\t}\n\t\treturn setterMethodName;\n\t}\n\n\t/**\n\t * Generates the getType method for cursor based on field. There're couple of\n     * unusual conditions. If field type is boolean, generate getInt method. If\n     * field type is char, generate getString method. If field type is Date, generate\n     * getLong method. If filed type is Integer, generate getInt method. If field type\n     * is bytes, generate getBlob method.\n\t * \n\t * @param field\n\t *            To generate getType method for cursor.\n\t * @return The getType method for cursor.\n\t */\n\tprivate String genGetColumnMethod(Field field) {\n        Class<?> fieldType;\n        if (isCollection(field.getType())) {\n            fieldType = getGenericTypeClass(field);\n        } else {\n            fieldType = field.getType();\n        }\n\t\treturn genGetColumnMethod(fieldType);\n\t}\n\n\t/**\n\t * Generates the getType method for cursor based on field. There're couple of\n\t * unusual conditions. If field type is boolean, generate getInt method. If\n\t * field type is char, generate getString method. If field type is Date, generate\n     * getLong method. If filed type is Integer, generate getInt method. If field type\n     * is bytes, generate getBlob method.\n\t * \n\t * @param fieldType\n\t *            To generate getType method for cursor.\n\t * @return The getType method for cursor.\n\t */\n\tprivate String genGetColumnMethod(Class<?> fieldType) {\n\t\tString typeName;\n\t\tif (fieldType.isPrimitive()) {\n\t\t\ttypeName = BaseUtility.capitalize(fieldType.getName());\n        } else {\n            typeName = fieldType.getSimpleName();\n        }\n\t\tString methodName = \"get\" + typeName;\n\t\tswitch (methodName) {\n\t\t\tcase \"getBoolean\":\n\t\t\tcase \"getInteger\":\n\t\t\t\tmethodName = \"getInt\";\n\t\t\t\tbreak;\n\t\t\tcase \"getChar\":\n\t\t\tcase \"getCharacter\":\n\t\t\t\tmethodName = \"getString\";\n\t\t\t\tbreak;\n\t\t\tcase \"getDate\":\n\t\t\t\tmethodName = \"getLong\";\n\t\t\t\tbreak;\n\t\t}\n\t\treturn methodName;\n\t}\n\n\t/**\n\t * Customize the passed in columns. If the columns contains an id column\n\t * already, just return it. If contains an _id column, rename it to id. If\n\t * not, an add id column then return. If it contains generic columns them\n     * from query and use them in supported generic fields.\n\t * \n\t * @param columns\n\t *            The original columns that passed in.\n\t * @param foreignKeyAssociations\n\t *            Associated classes which have foreign keys in the current\n\t *            model's table.\n\t * @return Customized columns with id column always.\n\t */\n\tprivate String[] getCustomizedColumns(String[] columns, List<Field> supportedGenericFields, List<AssociationsInfo> foreignKeyAssociations) {\n\t\tif (columns != null && columns.length > 0) {\n            boolean columnsContainsId = false;\n            List<String> convertList = Arrays.asList(columns);\n            List<String> columnList = new ArrayList<>(convertList);\n            List<String> supportedGenericFieldNames = new ArrayList<>();\n            List<Integer> columnToRemove = new ArrayList<>();\n            List<String> genericColumnsForQuery = new ArrayList<>();\n            List<Field> tempSupportedGenericFields = new ArrayList<>();\n\n            for (Field supportedGenericField : supportedGenericFields) {\n                supportedGenericFieldNames.add(supportedGenericField.getName());\n            }\n\n            for (int i = 0; i < columnList.size(); i++) {\n                String columnName = columnList.get(i);\n                // find out all generic columns.\n                if (BaseUtility.containsIgnoreCases(supportedGenericFieldNames, columnName)) {\n                    columnToRemove.add(i);\n                } else if (isIdColumn(columnName)) {\n                    columnsContainsId = true;\n                    if (\"_id\".equalsIgnoreCase(columnName)) {\n                        columnList.set(i, BaseUtility.changeCase(\"id\"));\n                    }\n                }\n            }\n\n            // remove generic columns cause they can't be used for query\n            for (int i = columnToRemove.size() - 1; i >= 0 ; i--) {\n                int index = columnToRemove.get(i);\n                String genericColumn = columnList.remove(index);\n                genericColumnsForQuery.add(genericColumn);\n            }\n\n            for (Field supportedGenericField : supportedGenericFields) {\n                String fieldName = supportedGenericField.getName();\n                if (BaseUtility.containsIgnoreCases(genericColumnsForQuery, fieldName)) {\n                    tempSupportedGenericFields.add(supportedGenericField);\n                }\n            }\n\n            supportedGenericFields.clear();\n            supportedGenericFields.addAll(tempSupportedGenericFields);\n\n            if (foreignKeyAssociations != null && foreignKeyAssociations.size() > 0) {\n\t\t\t\tfor (int i = 0; i < foreignKeyAssociations.size(); i++) {\n\t\t\t\t\tString associatedTable = DBUtility\n\t\t\t\t\t\t\t.getTableNameByClassName(foreignKeyAssociations.get(i)\n\t\t\t\t\t\t\t\t\t.getAssociatedClassName());\n                    columnList.add(getForeignKeyColumnName(associatedTable));\n\t\t\t\t}\n\t\t\t}\n            if (!columnsContainsId) {\n                columnList.add(BaseUtility.changeCase(\"id\"));\n            }\n\t\t\treturn columnList.toArray(new String[0]);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Analyze the associations for the specified class.\n\t * \n\t * @param className\n\t *            The full class name.\n\t */\n\tprivate void analyzeAssociations(String className) {\n\t\tCollection<AssociationsInfo> associationInfos = getAssociationInfo(className);\n\t\tif (fkInCurrentModel == null) {\n\t\t\tfkInCurrentModel = new ArrayList<>();\n\t\t} else {\n\t\t\tfkInCurrentModel.clear();\n\t\t}\n\t\tif (fkInOtherModel == null) {\n\t\t\tfkInOtherModel = new ArrayList<>();\n\t\t} else {\n\t\t\tfkInOtherModel.clear();\n\t\t}\n\t\tfor (AssociationsInfo associationInfo : associationInfos) {\n\t\t\tif (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE\n\t\t\t\t\t|| associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {\n\t\t\t\tif (associationInfo.getClassHoldsForeignKey().equals(className)) {\n\t\t\t\t\tfkInCurrentModel.add(associationInfo);\n\t\t\t\t} else {\n\t\t\t\t\tfkInOtherModel.add(associationInfo);\n\t\t\t\t}\n\t\t\t} else if (associationInfo.getAssociationType() == Const.Model.MANY_TO_MANY) {\n\t\t\t\tfkInOtherModel.add(associationInfo);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Finds the associated models of baseObj, then set them into baseObj.\n\t * \n\t * @param baseObj\n\t *            The class of base object.\n\t */\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tprivate void setAssociatedModel(LitePalSupport baseObj) {\n\t\tif (fkInOtherModel == null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (AssociationsInfo info : fkInOtherModel) {\n\t\t\tCursor cursor = null;\n\t\t\tString associatedClassName = info.getAssociatedClassName();\n\t\t\tboolean isM2M = info.getAssociationType() == Const.Model.MANY_TO_MANY;\n\t\t\ttry {\n\t\t\t\tList<Field> supportedFields = getSupportedFields(associatedClassName);\n                List<Field> supportedGenericFields = getSupportedGenericFields(associatedClassName);\n\t\t\t\tif (isM2M) {\n\t\t\t\t\tString tableName = baseObj.getTableName();\n\t\t\t\t\tString associatedTableName = DBUtility\n\t\t\t\t\t\t\t.getTableNameByClassName(associatedClassName);\n\t\t\t\t\tString intermediateTableName = DBUtility.getIntermediateTableName(tableName,\n\t\t\t\t\t\t\tassociatedTableName);\n\t\t\t\t\tStringBuilder sql = new StringBuilder();\n\t\t\t\t\tsql.append(\"select * from \").append(associatedTableName)\n\t\t\t\t\t\t\t.append(\" a inner join \").append(intermediateTableName)\n\t\t\t\t\t\t\t.append(\" b on a.id = b.\").append(associatedTableName).append(\"_id\")\n\t\t\t\t\t\t\t.append(\" where b.\").append(tableName).append(\"_id = ?\");\n\t\t\t\t\tcursor = Operator.findBySQL(BaseUtility.changeCase(sql.toString()),\n\t\t\t\t\t\t\tString.valueOf(baseObj.getBaseObjId()));\n\t\t\t\t} else {\n\t\t\t\t\tString foreignKeyColumn = getForeignKeyColumnName(DBUtility\n\t\t\t\t\t\t\t.getTableNameByClassName(info.getSelfClassName()));\n\t\t\t\t\tString associatedTableName = DBUtility\n\t\t\t\t\t\t\t.getTableNameByClassName(associatedClassName);\n\t\t\t\t\tcursor = mDatabase.query(BaseUtility.changeCase(associatedTableName), null,\n\t\t\t\t\t\t\tforeignKeyColumn + \"=?\",\n\t\t\t\t\t\t\tnew String[] { String.valueOf(baseObj.getBaseObjId()) }, null, null,\n\t\t\t\t\t\t\tnull, null);\n\t\t\t\t}\n\t\t\t\tif (cursor != null && cursor.moveToFirst()) {\n                    SparseArray<QueryInfoCache> queryInfoCacheSparseArray = new SparseArray<>();\n                    Map<Field, GenericModel> genericModelMap = new HashMap<>();\n\t\t\t\t\tdo {\n\t\t\t\t\t\tLitePalSupport modelInstance = (LitePalSupport) createInstanceFromClass(Class.forName(associatedClassName));\n\t\t\t\t\t\tgiveBaseObjIdValue(modelInstance,\n\t\t\t\t\t\t\t\tcursor.getLong(cursor.getColumnIndexOrThrow(\"id\")));\n\t\t\t\t\t\tsetValueToModel(modelInstance, supportedFields, null, cursor, queryInfoCacheSparseArray);\n                        setGenericValueToModel(modelInstance, supportedGenericFields, genericModelMap);\n\t\t\t\t\t\tif (info.getAssociationType() == Const.Model.MANY_TO_ONE || isM2M) {\n                            Field field = info.getAssociateOtherModelFromSelf();\n\t\t\t\t\t\t\tCollection collection = (Collection) getFieldValue(baseObj, field);\n                            if (collection == null) {\n                                if (isList(field.getType())) {\n                                    collection = new ArrayList();\n                                } else {\n                                    collection = new HashSet();\n                                }\n                                DynamicExecutor.setField(baseObj, field.getName(), collection, baseObj.getClass());\n                            }\n                            collection.add(modelInstance);\n\t\t\t\t\t\t} else if (info.getAssociationType() == Const.Model.ONE_TO_ONE) {\n\t\t\t\t\t\t\tsetFieldValue(baseObj,\n\t\t\t\t\t\t\t\t\tinfo.getAssociateOtherModelFromSelf(), modelInstance);\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (cursor.moveToNext());\n                    queryInfoCacheSparseArray.clear();\n                    genericModelMap.clear();\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t\t} finally {\n\t\t\t\tif (cursor != null) {\n\t\t\t\t\tcursor.close();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n    @SuppressWarnings(\"unchecked\")\n    private void setToModelByReflection(Object modelInstance, Field field, int columnIndex, String getMethodName, Cursor cursor)\n            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {\n        Class<?> cursorClass = cursor.getClass();\n        if (cursor.isNull(columnIndex)) return;\n        Method method = cursorClass.getMethod(getMethodName, int.class);\n        Object value = method.invoke(cursor, columnIndex);\n        if (field.getType() == boolean.class || field.getType() == Boolean.class) {\n            if (\"0\".equals(String.valueOf(value))) {\n                value = false;\n            } else if (\"1\".equals(String.valueOf(value))) {\n                value = true;\n            }\n        } else if (field.getType() == char.class || field.getType() == Character.class) {\n            value = ((String) value).charAt(0);\n        } else if (field.getType() == Date.class) {\n            long date = (long) value;\n            if (date == Long.MAX_VALUE) { // Long.MAX_VALUE is a date that will never reach, which represents null in our case.\n\t\t\t\tvalue = null;\n\t\t\t} else {\n\t\t\t\tvalue = new Date(date);\n\t\t\t}\n        }\n        if (isCollection(field.getType())) {\n            Collection<Object> collection = (Collection<Object>) DynamicExecutor.getField(modelInstance, field.getName(), modelInstance.getClass());\n            if (collection == null) {\n                if (isList(field.getType())) {\n                    collection = new ArrayList<>();\n                } else {\n                    collection = new HashSet<>();\n                }\n                DynamicExecutor.setField(modelInstance, field.getName(), collection, modelInstance.getClass());\n            }\n            String genericTypeName = getGenericTypeName(field);\n            if (\"java.lang.String\".equals(genericTypeName)) {\n                Encrypt annotation = field.getAnnotation(Encrypt.class);\n                if (annotation != null) {\n                    value = decryptValue(annotation.algorithm(), value);\n                }\n            } else if (modelInstance.getClass().getName().equals(genericTypeName)) {\n                if (value instanceof Long || value instanceof Integer) {\n                    value = Operator.find(modelInstance.getClass(), (long) value);\n                }\n            }\n            collection.add(value);\n        } else {\n            Encrypt annotation = field.getAnnotation(Encrypt.class);\n            if (annotation != null && \"java.lang.String\".equals(field.getType().getName())) {\n                value = decryptValue(annotation.algorithm(), value);\n            }\n            DynamicExecutor.setField(modelInstance, field.getName(), value,\n                    modelInstance.getClass());\n        }\n    }\n\n    /**\n     * Decrypt the field value with targeted algorithm.\n     * @param algorithm\n     *          The algorithm to decrypt value.\n     * @param fieldValue\n     *          Field value to decrypt.\n     * @return Decrypted value by targeted algorithm.\n     */\n    protected Object decryptValue(String algorithm, Object fieldValue) {\n        if (algorithm != null && fieldValue != null) {\n            if (LitePalSupport.AES.equalsIgnoreCase(algorithm)) {\n                fieldValue = CipherUtil.aesDecrypt((String) fieldValue);\n            }\n        }\n        return fieldValue;\n    }\n\n    /**\n     * Cache core info for query operation to improve query performance.\n     *\n     * @since 1.3.1\n     */\n\tstatic class QueryInfoCache {\n\n        String getMethodName;\n\n        Field field;\n\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/DeleteHandler.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport android.database.sqlite.SQLiteDatabase;\nimport android.text.TextUtils;\n\nimport org.litepal.Operator;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.Const;\nimport org.litepal.util.DBUtility;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * This is a component under LitePalSupport. It deals with the deleting stuff as\n * primary task. If deletes a saved model or delete a record with id, the\n * cascade delete function would work. But considering efficiency, if deletes\n * with deleteAll method, the referenced data in other tables will not be\n * affected. Developers should remove those referenced data by their own.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class DeleteHandler extends DataHandler {\n\n\t/**\n\t * To store associated tables of current model's table. Only used while\n\t * deleting by id and deleting all by model class.\n\t */\n\tprivate List<String> foreignKeyTableToDelete;\n\n\t/**\n\t * Initialize {@link org.litepal.crud.DataHandler#mDatabase} for operating database. Do not\n\t * allow to create instance of DeleteHandler out of CRUD package.\n\t * \n\t * @param db\n\t *            The instance of SQLiteDatabase.\n\t */\n    public DeleteHandler(SQLiteDatabase db) {\n\t\tmDatabase = db;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to delete. Using\n\t * baseObj to decide which record to delete. The baseObj must be saved\n\t * already(using {@link LitePalSupport#isSaved()} to test), or nothing will be\n\t * deleted. This method can action cascade delete. When the record is\n\t * deleted from database, all the referenced data such as foreign key value\n\t * will be removed too.\n\t * \n\t * @param baseObj\n\t *            The record to delete.\n\t * @return The number of rows affected. Including cascade delete rows.\n\t */\n\tint onDelete(LitePalSupport baseObj) {\n\t\tif (baseObj.isSaved()) {\n            List<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());\n            deleteGenericData(baseObj.getClass(), supportedGenericFields, baseObj.getBaseObjId());\n\t\t\tCollection<AssociationsInfo> associationInfos = analyzeAssociations(baseObj);\n\t\t\tint rowsAffected = deleteCascade(baseObj);\n\t\t\trowsAffected += mDatabase.delete(baseObj.getTableName(), \"id = \"\n\t\t\t\t\t+ baseObj.getBaseObjId(), null);\n\t\t\tclearAssociatedModelSaveState(baseObj, associationInfos);\n\t\t\treturn rowsAffected;\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to delete. Using\n\t * modelClass to decide which table to delete from, and id to decide a\n\t * specific row. This method can action cascade delete. When the record is\n\t * deleted from database, all the referenced data such as foreign key value\n\t * will be removed too.\n\t * \n\t * @param modelClass\n\t *            Which table to delete from.\n\t * @param id\n\t *            Which record to delete.\n\t * @return The number of rows affected. Including cascade delete rows.\n\t */\n    public int onDelete(Class<?> modelClass, long id) {\n        List<Field> supportedGenericFields = getSupportedGenericFields(modelClass.getName());\n        deleteGenericData(modelClass, supportedGenericFields, id);\n\t\tanalyzeAssociations(modelClass);\n\t\tint rowsAffected = deleteCascade(modelClass, id);\n\t\trowsAffected += mDatabase.delete(getTableName(modelClass),\n\t\t\t\t\"id = \" + id, null);\n\t\tgetForeignKeyTableToDelete().clear();\n\t\treturn rowsAffected;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to delete multiple\n\t * rows. Using tableName to decide which table to delete from, and\n\t * conditions representing the WHERE part of an SQL statement.\n\t * \n\t * @param tableName\n\t *            Which table to delete from.\n\t * @param conditions\n\t *            A string array representing the WHERE part of an SQL\n\t *            statement.\n\t * @return The number of rows affected.\n\t */\n\tpublic int onDeleteAll(String tableName, String... conditions) {\n\t\tBaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n\t\treturn mDatabase.delete(tableName, getWhereClause(conditions),\n\t\t\t\tgetWhereArgs(conditions));\n\t}\n\n    @SuppressWarnings(\"unchecked\")\n    public int onDeleteAll(Class<?> modelClass, String... conditions) {\n\t\tBaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n        List<Field> supportedGenericFields = getSupportedGenericFields(modelClass.getName());\n        if (!supportedGenericFields.isEmpty()) {\n            List<LitePalSupport> list = (List<LitePalSupport>) Operator.select(\"id\").where(conditions).find(modelClass);\n            if (list.size() > 0) {\n                long[] ids = new long[list.size()];\n                for (int i = 0; i < ids.length; i++) {\n                    LitePalSupport dataSupport = list.get(i);\n                    ids[i] = dataSupport.getBaseObjId();\n                }\n                deleteGenericData(modelClass, supportedGenericFields, ids);\n            }\n        }\n\t\tanalyzeAssociations(modelClass);\n\t\tint rowsAffected = deleteAllCascade(modelClass, conditions);\n\t\trowsAffected += mDatabase.delete(getTableName(modelClass), getWhereClause(conditions),\n\t\t\t\tgetWhereArgs(conditions));\n\t\tgetForeignKeyTableToDelete().clear();\n\t\treturn rowsAffected;\n\t}\n\n\t/**\n\t * Analyze the associations of modelClass and store the associated tables.\n\t * The associated tables might be used when deleting referenced data of a\n\t * specified row.\n\t * \n\t * @param modelClass\n\t *            To get associations of this class.\n\t */\n\tprivate void analyzeAssociations(Class<?> modelClass) {\n\t\tCollection<AssociationsInfo> associationInfos = getAssociationInfo(modelClass\n\t\t\t\t.getName());\n\t\tfor (AssociationsInfo associationInfo : associationInfos) {\n\t\t\tString associatedTableName = DBUtility\n\t\t\t\t\t.getTableNameByClassName(associationInfo\n\t\t\t\t\t\t\t.getAssociatedClassName());\n\t\t\tif (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE\n\t\t\t\t\t|| associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {\n\t\t\t\tString classHoldsForeignKey = associationInfo\n\t\t\t\t\t\t.getClassHoldsForeignKey();\n\t\t\t\tif (!modelClass.getName().equals(classHoldsForeignKey)) {\n\t\t\t\t\tgetForeignKeyTableToDelete().add(associatedTableName);\n\t\t\t\t}\n\t\t\t} else if (associationInfo.getAssociationType() == Const.Model.MANY_TO_MANY) {\n\t\t\t\tString joinTableName = DBUtility.getIntermediateTableName(\n\t\t\t\t\t\tgetTableName(modelClass), associatedTableName);\n\t\t\t\tjoinTableName = BaseUtility.changeCase(joinTableName);\n\t\t\t\tgetForeignKeyTableToDelete().add(joinTableName);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Use the analyzed result of associations to delete referenced data. So\n\t * this method must be called after {@link #analyzeAssociations(Class)}.\n\t * There're two parts of referenced data to delete. The foreign key rows in\n\t * associated table and the foreign key rows in intermediate join table.\n\t * \n\t * @param modelClass\n\t *            To get the table name and combine with id as a foreign key\n\t *            column.\n\t * @param id\n\t *            Delete all the rows which referenced with this id.\n\t * @return The number of rows affected in associated tables and intermediate\n\t *         join tables.\n\t */\n\tprivate int deleteCascade(Class<?> modelClass, long id) {\n\t\tint rowsAffected = 0;\n\t\tfor (String associatedTableName : getForeignKeyTableToDelete()) {\n\t\t\tString fkName = getForeignKeyColumnName(getTableName(modelClass));\n\t\t\trowsAffected += mDatabase.delete(associatedTableName, fkName\n\t\t\t\t\t+ \" = \" + id, null);\n\t\t}\n\t\treturn rowsAffected;\n\t}\n\n\tprivate int deleteAllCascade(Class<?> modelClass, String... conditions) {\n\t\tint rowsAffected = 0;\n\t\tfor (String associatedTableName : getForeignKeyTableToDelete()) {\n\t\t\tString tableName = getTableName(modelClass);\n\t\t\tString fkName = getForeignKeyColumnName(tableName);\n\t\t\tStringBuilder whereClause = new StringBuilder();\n\t\t\twhereClause.append(fkName).append(\" in (select id from \");\n\t\t\twhereClause.append(tableName);\n\t\t\tif (conditions != null && conditions.length > 0) {\n\t\t\t\twhereClause.append(\" where \").append(buildConditionString(conditions));\n\t\t\t}\n\t\t\twhereClause.append(\")\");\n\t\t\trowsAffected += mDatabase.delete(associatedTableName,\n\t\t\t\t\tBaseUtility.changeCase(whereClause.toString()), null);\n\t\t}\n\t\treturn rowsAffected;\n\t}\n\t\n\tprivate String buildConditionString(String... conditions) {\n\t\t  int argCount = conditions.length - 1;\n          String whereClause = conditions[0];\n          for (int i = 0; i < argCount; i++) {\n                  whereClause = whereClause.replaceFirst(\"\\\\?\", \"'\" + conditions[i+1] + \"'\");\n          }\n          return whereClause;\n\t}\n\n\t/**\n\t * Analyze the associations of baseObj and store the result in it. The\n\t * associations will be used when deleting referenced data of baseObj.\n\t * \n\t * @param baseObj\n\t *            The record to delete.\n\t */\n\tprivate Collection<AssociationsInfo> analyzeAssociations(LitePalSupport baseObj) {\n\t\ttry {\n\t\t\tCollection<AssociationsInfo> associationInfos = getAssociationInfo(baseObj\n\t\t\t\t\t.getClassName());\n\t\t\tanalyzeAssociatedModels(baseObj, associationInfos);\n\t\t\treturn associationInfos;\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Clear associated models' save state. After this method, the associated\n\t * models of baseObj which data is removed from database will become\n\t * unsaved.\n\t * \n\t * @param baseObj\n\t *            The record to delete.\n\t * @param associationInfos\n\t *            The associated info.\n\t */\n\tprivate void clearAssociatedModelSaveState(LitePalSupport baseObj,\n\t\t\tCollection<AssociationsInfo> associationInfos) {\n\t\ttry {\n\t\t\tfor (AssociationsInfo associationInfo : associationInfos) {\n\t\t\t\tif (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE\n\t\t\t\t\t\t&& !baseObj.getClassName().equals(\n\t\t\t\t\t\t\t\tassociationInfo.getClassHoldsForeignKey())) {\n\t\t\t\t\tCollection<LitePalSupport> associatedModels = getAssociatedModels(\n\t\t\t\t\t\t\tbaseObj, associationInfo);\n\t\t\t\t\tif (associatedModels != null && !associatedModels.isEmpty()) {\n\t\t\t\t\t\tfor (LitePalSupport model : associatedModels) {\n\t\t\t\t\t\t\tif (model != null) {\n\t\t\t\t\t\t\t\tmodel.clearSavedState();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {\n\t\t\t\t\tLitePalSupport model = getAssociatedModel(baseObj,\n\t\t\t\t\t\t\tassociationInfo);\n\t\t\t\t\tif (model != null) {\n\t\t\t\t\t\tmodel.clearSavedState();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Use the analyzed result of associations to delete referenced data. So\n\t * this method must be called after\n\t * {@link #analyzeAssociations(LitePalSupport)}. There're two parts of\n\t * referenced data to delete. The foreign key rows in associated tables and\n\t * the foreign key rows in intermediate join tables.\n\t * \n\t * @param baseObj\n\t *            The record to delete. Now contains associations info.\n\t * @return The number of rows affected in associated table and intermediate\n\t *         join table.\n\t */\n\tprivate int deleteCascade(LitePalSupport baseObj) {\n\t\tint rowsAffected;\n\t\trowsAffected = deleteAssociatedForeignKeyRows(baseObj);\n\t\trowsAffected += deleteAssociatedJoinTableRows(baseObj);\n\t\treturn rowsAffected;\n\t}\n\n\t/**\n\t * Delete the referenced rows of baseObj in associated tables(Many2One and\n\t * One2One conditions).\n\t * \n\t * @param baseObj\n\t *            The record to delete. Now contains associations info.\n\t * @return The number of rows affected in all associated tables.\n\t */\n\tprivate int deleteAssociatedForeignKeyRows(LitePalSupport baseObj) {\n\t\tint rowsAffected = 0;\n\t\tMap<String, Set<Long>> associatedModelMap = baseObj\n\t\t\t\t.getAssociatedModelsMapWithFK();\n\t\tfor (String associatedTableName : associatedModelMap.keySet()) {\n\t\t\tString fkName = getForeignKeyColumnName(baseObj.getTableName());\n\t\t\trowsAffected += mDatabase.delete(associatedTableName, fkName\n\t\t\t\t\t+ \" = \" + baseObj.getBaseObjId(), null);\n\t\t}\n\t\treturn rowsAffected;\n\t}\n\n\t/**\n\t * Delete the referenced rows of baseObj in intermediate join\n\t * tables(Many2Many condition).\n\t * \n\t * @param baseObj\n\t *            The record to delete. Now contains associations info.\n\t * @return The number of rows affected in all intermediate join tables.\n\t */\n\tprivate int deleteAssociatedJoinTableRows(LitePalSupport baseObj) {\n\t\tint rowsAffected = 0;\n\t\tSet<String> associatedTableNames = baseObj\n\t\t\t\t.getAssociatedModelsMapForJoinTable().keySet();\n\t\tfor (String associatedTableName : associatedTableNames) {\n\t\t\tString joinTableName = DBUtility.getIntermediateTableName(\n\t\t\t\t\tbaseObj.getTableName(), associatedTableName);\n\t\t\tString fkName = getForeignKeyColumnName(baseObj.getTableName());\n\t\t\trowsAffected += mDatabase.delete(joinTableName, fkName + \" = \"\n\t\t\t\t\t+ baseObj.getBaseObjId(), null);\n\t\t}\n\t\treturn rowsAffected;\n\t}\n\n\t/**\n\t * Get all the associated tables of current model's table. Only used while\n\t * deleting by id.\n\t * \n\t * @return All the associated tables of current model's table.\n\t */\n\tprivate List<String> getForeignKeyTableToDelete() {\n\t\tif (foreignKeyTableToDelete == null) {\n\t\t\tforeignKeyTableToDelete = new ArrayList<String>();\n\t\t}\n\t\treturn foreignKeyTableToDelete;\n\t}\n\n    /**\n     * Delete the generic data in generic tables while main data was deleted.\n     * @param modelClass\n     *          Used to get the generic table name and value id column.\n     * @param supportedGenericFields\n     *          List of all supported generic fields.\n     * @param ids\n     *          The id array of models.\n     */\n    private void deleteGenericData(Class<?> modelClass, List<Field> supportedGenericFields, long... ids) {\n        for (Field field : supportedGenericFields) {\n            String tableName = DBUtility.getGenericTableName(modelClass.getName(), field.getName());\n            String genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(modelClass.getName());\n            int maxExpressionCount = 500; // Prevent the where condition is too long\n            int length = ids.length;\n            int loopCount = (length - 1) / maxExpressionCount;\n            for (int i = 0; i <= loopCount; i++) {\n                StringBuilder whereClause = new StringBuilder();\n                boolean needOr = false;\n                for (int j = maxExpressionCount * i; j < maxExpressionCount * (i + 1); j++) {\n                    if (j >= length) {\n                        break;\n                    }\n                    long id = ids[j];\n                    if (needOr) {\n                        whereClause.append(\" or \");\n                    }\n                    whereClause.append(genericValueIdColumnName).append(\" = \").append(id);\n                    needOr = true;\n                }\n                if (!TextUtils.isEmpty(whereClause.toString())) {\n                    mDatabase.delete(tableName, whereClause.toString(), null);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/DynamicExecutor.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\nimport org.litepal.exceptions.LitePalSupportException;\n\n/**\n * This provides a send method to allow calling method in dynamic way. (Just\n * like Ruby, but not clean or easy as Ruby to use).\n * \n * @author Tony Green\n * @since 1.1\n */\nclass DynamicExecutor {\n\n\t/**\n\t * Disable to create an instance of DynamicExecutor.\n\t */\n\tprivate DynamicExecutor() {\n\t}\n\n\t/**\n\t * This method use java reflect API to execute method dynamically. Most\n\t * importantly, it could access the methods with private modifier to break\n\t * encapsulation.\n\t * \n\t * @param object\n\t *            The object to invoke method.\n\t * @param methodName\n\t *            The method name to invoke.\n\t * @param parameters\n\t *            The parameters.\n\t * @param objectClass\n\t *            Use objectClass to find method to invoke.\n\t * @param parameterTypes\n\t *            The parameter types.\n\t * @return Returns the result of dynamically invoking method.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\tstatic Object send(Object object, String methodName, Object[] parameters, Class<?> objectClass,\n\t\t\tClass<?>[] parameterTypes) throws SecurityException, IllegalArgumentException,\n\t\t\tIllegalAccessException, InvocationTargetException {\n\t\ttry {\n\t\t\tif (parameters == null) {\n\t\t\t\tparameters = new Object[] {};\n\t\t\t}\n\t\t\tif (parameterTypes == null) {\n\t\t\t\tparameterTypes = new Class[] {};\n\t\t\t}\n\t\t\tMethod method = objectClass.getDeclaredMethod(methodName, parameterTypes);\n\t\t\tmethod.setAccessible(true);\n\t\t\treturn method.invoke(object, parameters);\n\t\t} catch (NoSuchMethodException e) {\n\t\t\tthrow new LitePalSupportException(LitePalSupportException.noSuchMethodException(\n\t\t\t\t\tobjectClass.getSimpleName(), methodName), e);\n\t\t}\n\t}\n\n    static void set(Object object, String fieldName, Object value, Class<?> objectClass)\n            throws SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchFieldException {\n        Field objectField = objectClass.getDeclaredField(fieldName);\n        objectField.setAccessible(true);\n        objectField.set(object, value);\n    }\n\n\t/**\n\t * This method use java reflect API to set field value dynamically. Most\n\t * importantly, it could access fields with private modifier to break\n\t * encapsulation.\n\t * \n\t * @param object\n\t *            The object to access.\n\t * @param fieldName\n\t *            The field name to access.\n\t * @param value\n\t *            Assign this value to field.\n\t * @param objectClass\n\t *            The class of object.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws IllegalAccessException\n\t */\n\tstatic void setField(Object object, String fieldName, Object value, Class<?> objectClass)\n\t\t\tthrows SecurityException, IllegalArgumentException, IllegalAccessException {\n        if (objectClass == LitePalSupport.class || objectClass == Object.class) {\n            throw new LitePalSupportException(LitePalSupportException.noSuchFieldExceptioin(\n                    objectClass.getSimpleName(), fieldName));\n        }\n\t\ttry {\n\t\t\tset(object, fieldName, value, objectClass);\n\t\t} catch (NoSuchFieldException e) {\n\t\t\tsetField(object, fieldName, value, objectClass.getSuperclass());\n\t\t}\n\t}\n\n\t/**\n\t * This method use java reflect API to get field value dynamically. Most\n\t * importantly, it could access fields with private modifier to break\n\t * encapsulation.\n\t * \n\t * @param object\n\t *            The object to access.\n\t * @param fieldName\n\t *            The field name to access.\n\t * @param objectClass\n\t *            The class of object.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws IllegalAccessException\n\t */\n\tstatic Object getField(Object object, String fieldName, Class<?> objectClass)\n\t\t\tthrows IllegalArgumentException, IllegalAccessException {\n        if (objectClass == LitePalSupport.class || objectClass == Object.class) {\n            throw new LitePalSupportException(LitePalSupportException.noSuchFieldExceptioin(\n                    objectClass.getSimpleName(), fieldName));\n        }\n\t\ttry {\n\t\t\tField objectField = objectClass.getDeclaredField(fieldName);\n\t\t\tobjectField.setAccessible(true);\n\t\t\treturn objectField.get(object);\n\t\t} catch (NoSuchFieldException e) {\n\t\t\treturn getField(object, fieldName, objectClass.getSuperclass());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/LitePalSupport.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport android.database.sqlite.SQLiteDatabase;\n\nimport org.litepal.Operator;\nimport org.litepal.crud.async.SaveExecutor;\nimport org.litepal.crud.async.UpdateOrDeleteExecutor;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.DBUtility;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * LitePalSupport connects classes to SQLite database tables to establish an almost\n * zero-configuration persistence layer for applications. In the context of an\n * application, these classes are commonly referred to as models. Models can\n * also be connected to other models.<br>\n * LitePalSupport relies heavily on naming in that it uses class and association\n * names to establish mappings between respective database tables and foreign\n * key columns.<br>\n * Automated mapping between classes and tables, attributes and columns.\n * \n * <pre>\n * public class Person extends LitePalSupport {\n * \tprivate int id;\n * \tprivate String name;\n * \tprivate int age;\n * }\n * \n * The Person class is automatically mapped to the table named \"person\",\n * which might look like this:\n * \n * CREATE TABLE person (\n * \tid integer primary key autoincrement,\n * \tage integer, \n * \tname text\n * );\n * </pre>\n * \n * @author Tony Green\n * @since 2.0\n */\npublic class LitePalSupport {\n\n    /**\n     * Constant for MD5 encryption.\n     */\n    protected static final String MD5 = \"MD5\";\n\n    /**\n     * Constant for AES encryption.\n     */\n    protected static final String AES = \"AES\";\n\n\t/**\n\t * The identify of each model. LitePal will generate the value\n\t * automatically. Do not try to assign or modify it.\n\t */\n\tlong baseObjId;\n\n\t/**\n\t * A map contains all the associated models' id with M2O or O2O\n\t * associations. Each corresponding table of these models contains a foreign\n\t * key column.\n\t */\n    private Map<String, Set<Long>> associatedModelsMapWithFK;\n\n\t/**\n\t * A map contains all the associated models' id with M2O or O2O association.\n\t * Each corresponding table of these models doesn't contain foreign key\n\t * column. Instead self model has a foreign key column in the corresponding\n\t * table.\n\t */\n    private Map<String, Long> associatedModelsMapWithoutFK;\n\n\t/**\n\t * A map contains all the associated models' id with M2M association.\n\t */\n\tMap<String, List<Long>> associatedModelsMapForJoinTable;\n\n\t/**\n\t * When updating a model and the associations breaks between current model\n\t * and others, if current model holds a foreign key, it need to be cleared.\n\t * This list holds all the foreign key names that need to clear.\n\t */\n    private List<String> listToClearSelfFK;\n\n\t/**\n\t * When updating a model and the associations breaks between current model\n\t * and others, clear all the associated models' foreign key value if it\n\t * exists. This list holds all the associated table names that need to\n\t * clear.\n\t */\n    private List<String> listToClearAssociatedFK;\n\n\t/**\n\t * A list holds all the field names which need to be updated into default\n\t * value of model.\n\t */\n    private List<String> fieldsToSetToDefault;\n\n\t/**\n\t * Deletes the record in the database. The record must be saved already.<br>\n\t * The data in other tables which is referenced with the record will be\n\t * removed too.\n\t * \n\t * <pre>\n\t * Person person;\n\t * ....\n\t * if (person.isSaved()) {\n\t * \t\tperson.delete();\n\t * }\n\t * </pre>\n\t * \n\t * @return The number of rows affected. Including cascade delete rows.\n\t */\n\tpublic int delete() {\n\t    synchronized (LitePalSupport.class) {\n            SQLiteDatabase db = Connector.getDatabase();\n            db.beginTransaction();\n            try {\n                DeleteHandler deleteHandler = new DeleteHandler(db);\n                int rowsAffected = deleteHandler.onDelete(this);\n                baseObjId = 0;\n                db.setTransactionSuccessful();\n                return rowsAffected;\n            } finally {\n                db.endTransaction();\n            }\n        }\n\t}\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public UpdateOrDeleteExecutor deleteAsync() {\n        final UpdateOrDeleteExecutor executor = new UpdateOrDeleteExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int rowsAffected = delete();\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(rowsAffected);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Updates the corresponding record by id. Use setXxx to decide which\n\t * columns to update.\n\t * \n\t * <pre>\n\t * Person person = new Person();\n\t * person.setName(&quot;Jim&quot;);\n\t * person.update(1);\n\t * </pre>\n\t * \n\t * This means that the name of record 1 will be updated into Jim.<br>\n\t * \n\t * <b>Note: </b> 1. If you set a default value to a field, the corresponding\n\t * column won't be updated. Use {@link #setToDefault(String)} to update\n\t * columns into default value. 2. This method couldn't update foreign key in\n\t * database. So do not use setXxx to set associations between models.\n\t * \n\t * @param id\n\t *            Which record to update.\n\t * @return The number of rows affected.\n\t */\n\tpublic int update(long id) {\n        synchronized (LitePalSupport.class) {\n            SQLiteDatabase db = Connector.getDatabase();\n            db.beginTransaction();\n            try {\n                UpdateHandler updateHandler = new UpdateHandler(Connector.getDatabase());\n                int rowsAffected = updateHandler.onUpdate(this, id);\n                getFieldsToSetToDefault().clear();\n                db.setTransactionSuccessful();\n                return rowsAffected;\n            } catch (Exception e) {\n                throw new LitePalSupportException(e.getMessage(), e);\n            } finally {\n                db.endTransaction();\n            }\n        }\n\t}\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public UpdateOrDeleteExecutor updateAsync(final long id) {\n        final UpdateOrDeleteExecutor executor = new UpdateOrDeleteExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int rowsAffected = update(id);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(rowsAffected);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Updates all records with details given if they match a set of conditions\n\t * supplied. This method constructs a single SQL UPDATE statement and sends\n\t * it to the database.\n\t * \n\t * <pre>\n\t * Person person = new Person();\n\t * person.setName(&quot;Jim&quot;);\n\t * person.updateAll(&quot;name = ?&quot;, &quot;Tom&quot;);\n\t * </pre>\n\t * \n\t * This means that all the records which name is Tom will be updated into\n\t * Jim.<br>\n\t * \n\t * <b>Note: </b> 1. If you set a default value to a field, the corresponding\n\t * column won't be updated. Use {@link #setToDefault(String)} to update\n\t * columns into default value. 2. This method couldn't update foreign key in\n\t * database. So do not use setXxx to set associations between models.\n\t * \n\t * @param conditions\n\t *            A string array representing the WHERE part of an SQL\n\t *            statement. First parameter is the WHERE clause to apply when\n\t *            updating. The way of specifying place holders is to insert one\n\t *            or more question marks in the SQL. The first question mark is\n\t *            replaced by the second element of the array, the next question\n\t *            mark by the third, and so on. Passing empty string will update\n\t *            all rows.\n\t * @return The number of rows affected.\n\t */\n\tpublic int updateAll(String... conditions) {\n        synchronized (LitePalSupport.class) {\n            SQLiteDatabase db = Connector.getDatabase();\n            db.beginTransaction();\n            try {\n                UpdateHandler updateHandler = new UpdateHandler(Connector.getDatabase());\n                int rowsAffected = updateHandler.onUpdateAll(this, conditions);\n                getFieldsToSetToDefault().clear();\n                db.setTransactionSuccessful();\n                return rowsAffected;\n            } catch (Exception e) {\n                throw new LitePalSupportException(e.getMessage(), e);\n            } finally {\n                db.endTransaction();\n            }\n        }\n\t}\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public UpdateOrDeleteExecutor updateAllAsync(final String... conditions) {\n        final UpdateOrDeleteExecutor executor = new UpdateOrDeleteExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final int rowsAffected = updateAll(conditions);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(rowsAffected);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Saves the model. <br>\n\t * \n\t * <pre>\n\t * Person person = new Person();\n\t * person.setName(&quot;Tom&quot;);\n\t * person.setAge(22);\n\t * person.save();\n\t * </pre>\n\t * \n\t * If the model is a new record gets created in the database, otherwise the\n\t * existing record gets updated.<br>\n\t * If saving process failed by any accident, the whole action will be\n\t * cancelled and your database will be <b>rolled back</b>. <br>\n\t * If the model has a field named id or _id and field type is int or long,\n\t * the id value generated by database will assign to it after the model is\n\t * saved.<br>\n\t * Note that if the associated models of this model is already saved. The\n\t * associations between them will be built automatically in database after\n\t * it saved.\n\t * \n\t * @return If the model is saved successfully, return true. Any exception\n\t *         happens, return false.\n\t */\n\tpublic boolean save() {\n        try {\n            saveThrows();\n            return true;\n        } catch (Exception e) {\n            e.printStackTrace();\n            return false;\n        }\n\t}\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public SaveExecutor saveAsync() {\n        final SaveExecutor executor = new SaveExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final boolean success = save();\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(success);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n    /**\n\t * Saves the model. <br>\n\t * \n\t * <pre>\n\t * Person person = new Person();\n\t * person.setName(&quot;Tom&quot;);\n\t * person.setAge(22);\n\t * person.saveThrows();\n\t * </pre>\n\t * \n\t * If the model is a new record gets created in the database, otherwise the\n\t * existing record gets updated.<br>\n\t * If saving process failed by any accident, the whole action will be\n\t * cancelled and your database will be <b>rolled back</b> and throws\n\t * {@link LitePalSupportException}<br>\n\t * If the model has a field named id or _id and field type is int or long,\n\t * the id value generated by database will assign to it after the model is\n\t * saved.<br>\n\t * Note that if the associated models of this model is already saved. The\n\t * associations between them will be built automatically in database after\n\t * it saved.\n\t * \n\t * @throws LitePalSupportException\n\t */\n\tpublic void saveThrows() {\n        synchronized (LitePalSupport.class) {\n            SQLiteDatabase db = Connector.getDatabase();\n            db.beginTransaction();\n            try {\n                SaveHandler saveHandler = new SaveHandler(db);\n                saveHandler.onSave(this);\n                clearAssociatedData();\n                db.setTransactionSuccessful();\n            } catch (Exception e) {\n                throw new LitePalSupportException(e.getMessage(), e);\n            } finally {\n                db.endTransaction();\n            }\n        }\n\t}\n\n    /**\n     * Save the model if the conditions data not exist, or update the matching models if the conditions data exist. <br>\n     *\n     * <pre>\n     * Person person = new Person();\n     * person.setName(&quot;Tom&quot;);\n     * person.setAge(22);\n     * person.saveOrUpdate(&quot;name = ?&quot;, &quot;Tom&quot;);\n     * </pre>\n     *\n     * If person table doesn't have a name with Tom, a new record gets created in the database,\n     * otherwise all records which names are Tom will be updated.<br>\n     * If saving process failed by any accident, the whole action will be\n     * cancelled and your database will be <b>rolled back</b>. <br>\n     * If the model has a field named id or _id and field type is int or long,\n     * the id value generated by database will assign to it after the model is\n     * saved.<br>\n     * Note that if the associated models of this model is already saved. The\n     * associations between them will be built automatically in database after\n     * it saved.\n     *\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            updating. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return If the model saved or updated successfully, return true. Otherwise return false.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public boolean saveOrUpdate(String... conditions) {\n        synchronized (LitePalSupport.class) {\n            if (conditions == null || conditions.length == 0) {\n                return save();\n            }\n            List<LitePalSupport> list = (List<LitePalSupport>) Operator.where(conditions).find(getClass());\n            if (list.isEmpty()) {\n                return save();\n            } else {\n                SQLiteDatabase db = Connector.getDatabase();\n                db.beginTransaction();\n                try {\n                    for (LitePalSupport support : list) {\n                        baseObjId = support.getBaseObjId();\n                        SaveHandler saveHandler = new SaveHandler(db);\n                        saveHandler.onSave(this);\n                        clearAssociatedData();\n                    }\n                    db.setTransactionSuccessful();\n                    return true;\n                } catch (Exception e) {\n                    e.printStackTrace();\n                    return false;\n                } finally {\n                    db.endTransaction();\n                }\n            }\n        }\n    }\n\n    /**\n     * This method is deprecated and will be removed in the future releases.\n     * Handle async db operation in your own logic instead.\n     */\n    @Deprecated\n    public SaveExecutor saveOrUpdateAsync(final String... conditions) {\n        final SaveExecutor executor = new SaveExecutor();\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                synchronized (LitePalSupport.class) {\n                    final boolean success = saveOrUpdate(conditions);\n                    if (executor.getListener() != null) {\n                        Operator.getHandler().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                executor.getListener().onFinish(success);\n                            }\n                        });\n                    }\n                }\n            }\n        };\n        executor.submit(runnable);\n        return executor;\n    }\n\n\t/**\n\t * Current model is saved or not.\n\t * \n\t * @return If saved return true, or return false.\n\t */\n\tpublic boolean isSaved() {\n\t\treturn baseObjId > 0;\n\t}\n\n    /**\n     * It model is saved, clear the saved state and model becomes unsaved. Otherwise nothing will happen.\n     */\n    public void clearSavedState() {\n        baseObjId = 0;\n    }\n\n\t/**\n\t * When updating database with {@link LitePalSupport#update(long)}, you must\n\t * use this method to update a field into default value. Use setXxx with\n\t * default value of the model won't update anything. <br>\n\t * \n\t * @param fieldName\n\t *            The name of field to update into default value.\n\t */\n\tpublic void setToDefault(String fieldName) {\n\t\tgetFieldsToSetToDefault().add(fieldName);\n\t}\n\n    /**\n     * Assigns value to baseObjId. This will override the original value. <b>Never call this method\n     * unless you know exactly what you are doing.</b>\n     * @param baseObjId\n     *          Assigns value to baseObjId.\n     */\n    public void assignBaseObjId(long baseObjId) {\n        this.baseObjId = baseObjId;\n    }\n\n\t/**\n\t * Disable developers to create instance of LitePalSupport directly. They\n\t * should inherit this class with subclasses and operate on them.\n\t */\n\tprotected LitePalSupport() {\n\t}\n\n\t/**\n\t * Get the baseObjId of this model if it's useful for developers. It's for\n\t * system use usually. Do not try to assign or modify it.\n\t * \n\t * @return The base object id.\n\t */\n\tprotected long getBaseObjId() {\n\t\treturn baseObjId;\n\t}\n\t\n\t/**\n\t * Get the full class name of self.\n\t * \n\t * @return The full class name of self.\n\t */\n\tprotected String getClassName() {\n\t\treturn getClass().getName();\n\t}\n\n\t/**\n\t * Get the corresponding table name of current model.\n\t * \n\t * @return The corresponding table name of current model.\n\t */\n\tprotected String getTableName() {\n\t\treturn BaseUtility.changeCase(DBUtility.getTableNameByClassName(getClassName()));\n\t}\n\n\t/**\n\t * Get the list which holds all field names to update them into default\n\t * value of model in database.\n\t * \n\t * @return List holds all the field names which need to be updated into\n\t *         default value of model.\n\t */\n\tList<String> getFieldsToSetToDefault() {\n\t\tif (fieldsToSetToDefault == null) {\n\t\t\tfieldsToSetToDefault = new ArrayList<String>();\n\t\t}\n\t\treturn fieldsToSetToDefault;\n\t}\n\n\t/**\n\t * Add the id of an associated model into self model's associatedIdsWithFK\n\t * map. The associated model has a foreign key column in the corresponding\n\t * table.\n\t * \n\t * @param associatedTableName\n\t *            The table name of associated model.\n\t * @param associatedId\n\t *            The {@link #baseObjId} of associated model after it is saved.\n\t */\n\tvoid addAssociatedModelWithFK(String associatedTableName, long associatedId) {\n\t\tSet<Long> associatedIdsWithFKSet = getAssociatedModelsMapWithFK().get(associatedTableName);\n\t\tif (associatedIdsWithFKSet == null) {\n\t\t\tassociatedIdsWithFKSet = new HashSet<Long>();\n\t\t\tassociatedIdsWithFKSet.add(associatedId);\n\t\t\tassociatedModelsMapWithFK.put(associatedTableName, associatedIdsWithFKSet);\n\t\t} else {\n\t\t\tassociatedIdsWithFKSet.add(associatedId);\n\t\t}\n\t}\n\n\t/**\n\t * Get the associated model's map of self model. It can be used for\n\t * associations actions of CRUD. The key is the name of associated model.\n\t * The value is a List of id of associated models.\n\t * \n\t * @return An associated model's map to update all the foreign key columns\n\t *         of associated models' table with self model's id.\n\t */\n\tMap<String, Set<Long>> getAssociatedModelsMapWithFK() {\n\t\tif (associatedModelsMapWithFK == null) {\n\t\t\tassociatedModelsMapWithFK = new HashMap<String, Set<Long>>();\n\t\t}\n\t\treturn associatedModelsMapWithFK;\n\t}\n\n\t/**\n\t * Add the id of an associated model into self model's associatedIdsM2M map.\n\t * \n\t * @param associatedModelName\n\t *            The name of associated model.\n\t * @param associatedId\n\t *            The id of associated model.\n\t */\n\tvoid addAssociatedModelForJoinTable(String associatedModelName, long associatedId) {\n\t\tList<Long> associatedIdsM2MSet = getAssociatedModelsMapForJoinTable().get(\n\t\t\t\tassociatedModelName);\n\t\tif (associatedIdsM2MSet == null) {\n\t\t\tassociatedIdsM2MSet = new ArrayList<Long>();\n\t\t\tassociatedIdsM2MSet.add(associatedId);\n\t\t\tassociatedModelsMapForJoinTable.put(associatedModelName, associatedIdsM2MSet);\n\t\t} else {\n\t\t\tassociatedIdsM2MSet.add(associatedId);\n\t\t}\n\t}\n\n\t/**\n\t * Add an empty Set into {@link #associatedModelsMapForJoinTable} with\n\t * associated model name as key. Might be useful when comes to update\n\t * intermediate join table.\n\t * \n\t * @param associatedModelName\n\t *            The name of associated model.\n\t */\n\tvoid addEmptyModelForJoinTable(String associatedModelName) {\n\t\tList<Long> associatedIdsM2MSet = getAssociatedModelsMapForJoinTable().get(\n\t\t\t\tassociatedModelName);\n\t\tif (associatedIdsM2MSet == null) {\n\t\t\tassociatedIdsM2MSet = new ArrayList<Long>();\n\t\t\tassociatedModelsMapForJoinTable.put(associatedModelName, associatedIdsM2MSet);\n\t\t}\n\t}\n\n\t/**\n\t * Get the associated model's map for intermediate join table. It is used to\n\t * save values into intermediate join table. The key is the name of\n\t * associated model. The value is the id of associated model.\n\t * \n\t * @return An associated model's map to save values into intermediate join\n\t *         table\n\t */\n\tMap<String, List<Long>> getAssociatedModelsMapForJoinTable() {\n\t\tif (associatedModelsMapForJoinTable == null) {\n\t\t\tassociatedModelsMapForJoinTable = new HashMap<String, List<Long>>();\n\t\t}\n\t\treturn associatedModelsMapForJoinTable;\n\t}\n\n\t/**\n\t * Add the id of an associated model into self model's association\n\t * collection. The associated model doesn't have a foreign key column in the\n\t * corresponding table. Instead self model has a foreign key column in the\n\t * corresponding table.\n\t * \n\t * @param associatedTableName\n\t *            The simple class name of associated model.\n\t * @param associatedId\n\t *            The {@link #baseObjId} of associated model after it is saved.\n\t */\n\tvoid addAssociatedModelWithoutFK(String associatedTableName, long associatedId) {\n\t\tgetAssociatedModelsMapWithoutFK().put(associatedTableName, associatedId);\n\t}\n\n\t/**\n\t * Get the associated model's map of self model. It can be used for\n\t * associations actions of CRUD. The key is the name of associated model's\n\t * table. The value is the id of associated model.\n\t * \n\t * @return An associated model's map to save self model with foreign key.\n\t */\n\tMap<String, Long> getAssociatedModelsMapWithoutFK() {\n\t\tif (associatedModelsMapWithoutFK == null) {\n\t\t\tassociatedModelsMapWithoutFK = new HashMap<String, Long>();\n\t\t}\n\t\treturn associatedModelsMapWithoutFK;\n\t}\n\n\t/**\n\t * Add a foreign key name into the clear list.\n\t * \n\t * @param foreignKeyName\n\t *            The name of foreign key.\n\t */\n\tvoid addFKNameToClearSelf(String foreignKeyName) {\n\t\tList<String> list = getListToClearSelfFK();\n\t\tif (!list.contains(foreignKeyName)) {\n\t\t\tlist.add(foreignKeyName);\n\t\t}\n\t}\n\n\t/**\n\t * Get the foreign key name list to clear foreign key value in current\n\t * model's table.\n\t * \n\t * @return The list of foreign key names to clear in current model's table.\n\t */\n\tList<String> getListToClearSelfFK() {\n\t\tif (listToClearSelfFK == null) {\n\t\t\tlistToClearSelfFK = new ArrayList<String>();\n\t\t}\n\t\treturn listToClearSelfFK;\n\t}\n\n\t/**\n\t * Add an associated table name into the list to clear.\n\t * \n\t * @param associatedTableName\n\t *            The name of associated table.\n\t */\n\tvoid addAssociatedTableNameToClearFK(String associatedTableName) {\n\t\tList<String> list = getListToClearAssociatedFK();\n\t\tif (!list.contains(associatedTableName)) {\n\t\t\tlist.add(associatedTableName);\n\t\t}\n\t}\n\n\t/**\n\t * Get the associated table names list which need to clear their foreign key\n\t * values.\n\t * \n\t * @return The list with associated table names to clear foreign key values.\n\t */\n\tList<String> getListToClearAssociatedFK() {\n\t\tif (listToClearAssociatedFK == null) {\n\t\t\tlistToClearAssociatedFK = new ArrayList<String>();\n\t\t}\n\t\treturn listToClearAssociatedFK;\n\t}\n\n\t/**\n\t * Clear all the data for storing associated models' data.\n\t */\n\tvoid clearAssociatedData() {\n\t\tclearIdOfModelWithFK();\n\t\tclearIdOfModelWithoutFK();\n\t\tclearIdOfModelForJoinTable();\n\t\tclearFKNameList();\n\t}\n\n\t/**\n\t * Clear all the data in {@link #associatedModelsMapWithFK}.\n\t */\n\tprivate void clearIdOfModelWithFK() {\n\t\tfor (String associatedModelName : getAssociatedModelsMapWithFK().keySet()) {\n\t\t\tassociatedModelsMapWithFK.get(associatedModelName).clear();\n\t\t}\n\t\tassociatedModelsMapWithFK.clear();\n\t}\n\n\t/**\n\t * Clear all the data in {@link #associatedModelsMapWithoutFK}.\n\t */\n\tprivate void clearIdOfModelWithoutFK() {\n\t\tgetAssociatedModelsMapWithoutFK().clear();\n\t}\n\n\t/**\n\t * Clear all the data in {@link #associatedModelsMapForJoinTable}.\n\t */\n\tprivate void clearIdOfModelForJoinTable() {\n\t\tfor (String associatedModelName : getAssociatedModelsMapForJoinTable().keySet()) {\n\t\t\tassociatedModelsMapForJoinTable.get(associatedModelName).clear();\n\t\t}\n\t\tassociatedModelsMapForJoinTable.clear();\n\t}\n\n\t/**\n\t * Clear all the data in {@link #listToClearSelfFK}.\n\t */\n\tprivate void clearFKNameList() {\n\t\tgetListToClearSelfFK().clear();\n\t\tgetListToClearAssociatedFK().clear();\n\t}\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/Many2ManyAnalyzer.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Collection;\n\nimport org.litepal.LitePalBase;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.DBUtility;\n\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\n\n/**\n * Deals analysis work when comes to two models are associated with Many2Many\n * associations.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class Many2ManyAnalyzer extends AssociationsAnalyzer {\n\n\t/**\n\t * Analyzing the AssociationInfo. It will help baseObj assign the necessary\n\t * values automatically. If the two associated models have bidirectional\n\t * associations in class files but developer has only build unidirectional\n\t * associations in models, it will force to build the bidirectional\n\t * associations. Besides the\n\t * {@link LitePalSupport#addAssociatedModelForJoinTable(String, long)} will be called\n\t * here to put right values into tables.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associationInfo\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\tvoid analyze(LitePalSupport baseObj, AssociationsInfo associationInfo) throws SecurityException,\n\t\t\tIllegalArgumentException, NoSuchMethodException, IllegalAccessException,\n\t\t\tInvocationTargetException {\n\t\tCollection<LitePalSupport> associatedModels = getAssociatedModels(baseObj, associationInfo);\n\t\tdeclareAssociations(baseObj, associationInfo);\n\t\tif (associatedModels != null) {\n\t\t\tfor (LitePalSupport associatedModel : associatedModels) {\n\t\t\t\tCollection<LitePalSupport> tempCollection = getReverseAssociatedModels(\n\t\t\t\t\t\tassociatedModel, associationInfo);\n\t\t\t\tCollection<LitePalSupport> reverseAssociatedModels = checkAssociatedModelCollection(\n\t\t\t\t\t\ttempCollection, associationInfo.getAssociateSelfFromOtherModel());\n\t\t\t\taddNewModelForAssociatedModel(reverseAssociatedModels, baseObj);\n\t\t\t\tsetReverseAssociatedModels(associatedModel, associationInfo,\n\t\t\t\t\t\treverseAssociatedModels);\n\t\t\t\tdealAssociatedModel(baseObj, associatedModel);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * This add an empty set for {@link LitePalSupport#associatedModelsMapForJoinTable}.\n     * Might use for updating intermediate join table.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associationInfo\n\t *            To get associated table name from.\n\t */\n\tprivate void declareAssociations(LitePalSupport baseObj, AssociationsInfo associationInfo) {\n\t\tbaseObj.addEmptyModelForJoinTable(getAssociatedTableName(associationInfo));\n\t}\n\n\t/**\n\t * Force to build bidirectional associations for the associated model. If it\n\t * has already built, ignoring the rest process.\n\t * \n\t * @param associatedModelCollection\n\t *            The associated models collection of the associated model. Add\n\t *            self model into it if it doesn't contain self model yet.\n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t */\n\tprivate void addNewModelForAssociatedModel(Collection<LitePalSupport> associatedModelCollection,\n\t\t\tLitePalSupport baseObj) {\n\t\tif (!associatedModelCollection.contains(baseObj)) {\n\t\t\tassociatedModelCollection.add(baseObj);\n\t\t}\n\t}\n\n\t/**\n\t * First of all the associated model need to be saved already, or nothing\n\t * will be executed below. Then add the id of associated model into\n\t * {@link LitePalSupport#associatedModelsMapForJoinTable} for\n     * inserting value into intermediate join table after baseObj is saved.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t */\n\tprivate void dealAssociatedModel(LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\tif (associatedModel.isSaved()) {\n\t\t\tbaseObj.addAssociatedModelForJoinTable(associatedModel.getTableName(),\n\t\t\t\t\tassociatedModel.getBaseObjId());\n\t\t}\n\t}\n\n\t/**\n\t * Get the associated table name by {@link org.litepal.crud.model.AssociationsInfo} after case\n\t * changed.\n\t * \n\t * @param associationInfo\n\t *            To get the associated table name from.\n\t * @return The name of associated table with changed case.\n\t */\n\tprivate String getAssociatedTableName(AssociationsInfo associationInfo) {\n\t\treturn BaseUtility.changeCase(DBUtility.getTableNameByClassName(associationInfo\n\t\t\t\t.getAssociatedClassName()));\n\t}\n\n\t/**\n\t * Check if the associations between self model and associated model is\n\t * already saved into intermediate join table.<br>\n\t * Make sure baseObj and associatedModel are saved already, or the result\n\t * might be wrong.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t * @return If the associations between them is saved into intermediate join\n\t *         table, return true. Otherwise return false.\n\t */\n\t@SuppressWarnings(\"unused\")\n\t@Deprecated\n\tprivate boolean isDataExists(LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\tboolean exists = false;\n\t\tSQLiteDatabase db = Connector.getDatabase();\n\t\tCursor cursor = null;\n\t\ttry {\n\t\t\tcursor = db.query(getJoinTableName(baseObj, associatedModel), null,\n\t\t\t\t\tgetSelection(baseObj, associatedModel),\n\t\t\t\t\tgetSelectionArgs(baseObj, associatedModel), null, null, null);\n\t\t\texists = cursor.getCount() > 0;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn true;\n\t\t} finally {\n\t\t\tcursor.close();\n\t\t}\n\t\treturn exists;\n\t}\n\n\t/**\n\t * Build the selection for querying the data in table. Column names are the\n\t * table names with _id as suffix.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t * @return The selection clause for querying data.\n\t */\n\tprivate String getSelection(LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\tStringBuilder where = new StringBuilder();\n\t\twhere.append(getForeignKeyColumnName(baseObj.getTableName()));\n\t\twhere.append(\" = ? and \");\n\t\twhere.append(getForeignKeyColumnName(associatedModel.getTableName()));\n\t\twhere.append(\" = ?\");\n\t\treturn where.toString();\n\t}\n\n\t/**\n\t * Build the selection arguments to fill selection clause.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t * @return The selection arguments with the id of baseObj and\n\t *         associatedModel to fill.\n\t */\n\tprivate String[] getSelectionArgs(LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\treturn new String[] { String.valueOf(baseObj.getBaseObjId()),\n\t\t\t\tString.valueOf(associatedModel.getBaseObjId()) };\n\t}\n\n\t/**\n\t * Get the intermediate join table name for self model and associated model.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t * @return The intermediate join table name.\n\t */\n\tprivate String getJoinTableName(LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\treturn getIntermediateTableName(baseObj, associatedModel.getTableName());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/Many2OneAnalyzer.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Collection;\n\nimport org.litepal.LitePalBase;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.util.DBUtility;\n\n/**\n * Deals analysis work when comes to two models are associated with Many2One\n * associations.\n * \n * @author Tony Green\n * @since 1.1\n */\nclass Many2OneAnalyzer extends AssociationsAnalyzer {\n\n\t/**\n\t * Analyzing the AssociationInfo. It will help baseObj assign the necessary\n\t * values automatically. If the two associated models have bidirectional\n\t * associations in class files but developer has only build unidirectional\n\t * associations in models, it will force to build the bidirectional\n\t * associations. Besides\n\t * {@link LitePalSupport#addAssociatedModelWithFK(String, long)} and\n\t * {@link LitePalSupport#addAssociatedModelWithoutFK(String, long)} will be\n\t * called here to put right values into tables.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist.\n\t * @param associationInfo\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\tvoid analyze(LitePalSupport baseObj, AssociationsInfo associationInfo) throws SecurityException,\n\t\t\tIllegalArgumentException, NoSuchMethodException, IllegalAccessException,\n\t\t\tInvocationTargetException {\n\t\tif (baseObj.getClassName().equals(associationInfo.getClassHoldsForeignKey())) {\n\t\t\tanalyzeManySide(baseObj, associationInfo);\n\t\t} else {\n\t\t\tanalyzeOneSide(baseObj, associationInfo);\n\t\t}\n\t}\n\n\t/**\n\t * When it's on the M side. Get the associated model first, then use it to\n\t * get the associated model collection on the O side. Initialize the\n\t * collection by calling\n\t * {@link #checkAssociatedModelCollection(java.util.Collection, java.lang.reflect.Field)}\n\t * and calling\n\t * {@link #dealAssociatedModelOnManySide(java.util.Collection, LitePalSupport, LitePalSupport)}\n\t * to set foreign key.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associationInfo\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\tprivate void analyzeManySide(LitePalSupport baseObj, AssociationsInfo associationInfo)\n\t\t\tthrows SecurityException, IllegalArgumentException, NoSuchMethodException,\n\t\t\tIllegalAccessException, InvocationTargetException {\n\t\tLitePalSupport associatedModel = getAssociatedModel(baseObj, associationInfo);\n\t\tif (associatedModel != null) {\n\t\t\t// now it's m2o bidirectional association.\n\t\t\tCollection<LitePalSupport> tempCollection = getReverseAssociatedModels(associatedModel,\n\t\t\t\t\tassociationInfo);\n\t\t\tCollection<LitePalSupport> reverseAssociatedModels = checkAssociatedModelCollection(\n\t\t\t\t\ttempCollection, associationInfo.getAssociateSelfFromOtherModel());\n\t\t\tsetReverseAssociatedModels(associatedModel, associationInfo, reverseAssociatedModels);\n\t\t\tdealAssociatedModelOnManySide(reverseAssociatedModels, baseObj, associatedModel);\n\t\t} else {\n\t\t\tmightClearFKValue(baseObj, associationInfo);\n\t\t}\n\t}\n\n\t/**\n\t * When it's on the O side. Get the associated model collection first, then\n\t * iterate all the associated models. Each associated model calls\n\t * {@link #buildBidirectionalAssociations(LitePalSupport, LitePalSupport, org.litepal.crud.model.AssociationsInfo)}\n\t * to build bidirectional association if they haven't built yet. Then calls\n\t * {@link #dealAssociatedModelOnOneSide(LitePalSupport, LitePalSupport)} to set\n\t * foreign key.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist.\n\t * @param associationInfo\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\tprivate void analyzeOneSide(LitePalSupport baseObj, AssociationsInfo associationInfo)\n\t\t\tthrows SecurityException, IllegalArgumentException, NoSuchMethodException,\n\t\t\tIllegalAccessException, InvocationTargetException {\n\t\tCollection<LitePalSupport> associatedModels = getAssociatedModels(baseObj, associationInfo);\n\t\tif (associatedModels == null || associatedModels.isEmpty()) {\n\t\t\tString tableName = DBUtility.getTableNameByClassName(associationInfo\n\t\t\t\t\t.getAssociatedClassName());\n\t\t\tbaseObj.addAssociatedTableNameToClearFK(tableName);\n\t\t\treturn;\n\t\t}\n\t\tfor (LitePalSupport associatedModel : associatedModels) {\n\t\t\tbuildBidirectionalAssociations(baseObj, associatedModel, associationInfo);\n\t\t\tdealAssociatedModelOnOneSide(baseObj, associatedModel);\n\t\t}\n\t}\n\n\t/**\n\t * Check if the baseObj is already existed in the associatedModels\n\t * collection. If not add baseObj into the collection. Then if the\n\t * associated model is saved, add its' name and id to baseObj by calling\n\t * {@link LitePalSupport#addAssociatedModelWithFK(String, long)}.\n\t * \n\t * @param associatedModels\n\t *            The associated model collection.\n\t * @param baseObj\n\t *            The baseObj currently want to persist.\n\t * @param associatedModel\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t */\n\tprivate void dealAssociatedModelOnManySide(Collection<LitePalSupport> associatedModels,\n                                               LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\tif (!associatedModels.contains(baseObj)) {\n\t\t\tassociatedModels.add(baseObj);\n\t\t}\n\t\tif (associatedModel.isSaved()) {\n\t\t\tbaseObj.addAssociatedModelWithoutFK(associatedModel.getTableName(),\n\t\t\t\t\tassociatedModel.getBaseObjId());\n\t\t}\n\t}\n\n\t/**\n\t * Deals with associated model on one side.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist.\n\t * @param associatedModel\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t */\n\tprivate void dealAssociatedModelOnOneSide(LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\tdealsAssociationsOnTheSideWithoutFK(baseObj, associatedModel);\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/One2OneAnalyzer.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport java.lang.reflect.InvocationTargetException;\n\nimport org.litepal.LitePalBase;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.util.DBUtility;\n\n/**\n * Deals analysis work when comes to two models are associated with One2One\n * associations.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class One2OneAnalyzer extends AssociationsAnalyzer {\n\n\t/**\n\t * Analyzing the AssociationInfo. It will help baseObj assign the necessary\n\t * values automatically. If the two associated models have bidirectional\n\t * associations in class files but developer has only build unidirectional\n\t * associations in models, it will force to build the bidirectional\n\t * associations. Besides\n\t * {@link LitePalSupport#addAssociatedModelWithFK(String, long)} and\n\t * {@link LitePalSupport#addAssociatedModelWithoutFK(String, long)} will be\n\t * called here to put right values into tables.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist or update.\n\t * @param associationInfo\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t * @throws SecurityException\n\t * @throws IllegalArgumentException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalAccessException\n\t * @throws java.lang.reflect.InvocationTargetException\n\t */\n\tvoid analyze(LitePalSupport baseObj, AssociationsInfo associationInfo) throws SecurityException,\n\t\t\tIllegalArgumentException, NoSuchMethodException, IllegalAccessException,\n\t\t\tInvocationTargetException {\n\t\tLitePalSupport associatedModel = getAssociatedModel(baseObj, associationInfo);\n\t\tif (associatedModel != null) {\n\t\t\tbuildBidirectionalAssociations(baseObj, associatedModel, associationInfo);\n\t\t\tdealAssociatedModel(baseObj, associatedModel, associationInfo);\n\t\t} else {\n\t\t\tString tableName = DBUtility.getTableNameByClassName(associationInfo\n\t\t\t\t\t.getAssociatedClassName());\n\t\t\tbaseObj.addAssociatedTableNameToClearFK(tableName);\n\t\t}\n\t}\n\n\t/**\n\t * Check the association type. If it's bidirectional association, calls\n\t * {@link #bidirectionalCondition(LitePalSupport, LitePalSupport)}. If it's\n\t * unidirectional association, calls\n\t * {@link #unidirectionalCondition(LitePalSupport, LitePalSupport)}.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist.\n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t * @param associationInfo\n\t *            The associated info analyzed by\n\t *            {@link LitePalBase#getAssociationInfo(String)}.\n\t */\n\tprivate void dealAssociatedModel(LitePalSupport baseObj, LitePalSupport associatedModel,\n                                     AssociationsInfo associationInfo) {\n\t\tif (associationInfo.getAssociateSelfFromOtherModel() != null) {\n\t\t\tbidirectionalCondition(baseObj, associatedModel);\n\t\t} else {\n\t\t\tunidirectionalCondition(baseObj, associatedModel);\n\t\t}\n\t}\n\n\t/**\n\t * Deals bidirectional association condition. If associated model is saved,\n\t * add its' name and id to baseObj by calling\n\t * {@link LitePalSupport#addAssociatedModelWithFK(String, long)}. Add its' name\n\t * and id to baseObj by calling\n\t * {@link LitePalSupport#addAssociatedModelWithoutFK(String, long)}.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist.\n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t */\n\tprivate void bidirectionalCondition(LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\tif (associatedModel.isSaved()) {\n\t\t\t// use to update associated table after saving\n\t\t\tbaseObj.addAssociatedModelWithFK(associatedModel.getTableName(),\n\t\t\t\t\tassociatedModel.getBaseObjId());\n\t\t\t// use to add foreign key value while saving\n\t\t\tbaseObj.addAssociatedModelWithoutFK(associatedModel.getTableName(),\n\t\t\t\t\tassociatedModel.getBaseObjId());\n\t\t}\n\t}\n\n\t/**\n\t * Deals unidirectional associations condition.\n\t * \n\t * @param baseObj\n\t *            The baseObj currently want to persist.\n\t * @param associatedModel\n\t *            The associated model of baseObj.\n\t */\n\tprivate void unidirectionalCondition(LitePalSupport baseObj, LitePalSupport associatedModel) {\n\t\tdealsAssociationsOnTheSideWithoutFK(baseObj, associatedModel);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/QueryHandler.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport java.util.List;\n\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.DBUtility;\n\nimport android.database.sqlite.SQLiteDatabase;\n\n/**\n * This is a component under LitePalSupport. It deals with query stuff as primary\n * task.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class QueryHandler extends DataHandler {\n\n\t/**\n\t * Initialize {@link org.litepal.crud.DataHandler#mDatabase} for operating database. Do not\n\t * allow to create instance of QueryHandler out of CRUD package.\n\t * \n\t * @param db\n\t *            The instance of SQLiteDatabase.\n\t */\n    public QueryHandler(SQLiteDatabase db) {\n\t\tmDatabase = db;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to query a record\n\t * based on id. If the result set is empty, gives null back.\n\t * \n\t * @param modelClass\n\t *            Which table to query and the object type to return.\n\t * @param id\n\t *            Which record to query.\n\t * @param isEager\n\t *            True to load the associated models, false not.\n\t * @return An object with found data from database, or null.\n\t */\n    public <T> T onFind(Class<T> modelClass, long id, boolean isEager) {\n\t\tList<T> dataList = query(modelClass, null, \"id = ?\", new String[] { String.valueOf(id) },\n\t\t\t\tnull, null, null, null, getForeignKeyAssociations(modelClass.getName(), isEager));\n\t\tif (dataList.size() > 0) {\n\t\t\treturn dataList.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to query the first\n\t * record in a table. If the result set is empty, gives null back.\n\t * \n\t * @param modelClass\n\t *            Which table to query and the object type to return.\n\t * @param isEager\n\t *            True to load the associated models, false not.\n\t * @return An object with data of first row, or null.\n\t */\n    public <T> T onFindFirst(Class<T> modelClass, boolean isEager) {\n\t\tList<T> dataList = query(modelClass, null, null, null, null, null, \"id\", \"1\",\n\t\t\t\tgetForeignKeyAssociations(modelClass.getName(), isEager));\n\t\tif (dataList.size() > 0) {\n\t\t\treturn dataList.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to query the last\n\t * record in a table. If the result set is empty, gives null back.\n\t * \n\t * @param modelClass\n\t *            Which table to query and the object type to return.\n\t * @param isEager\n\t *            True to load the associated models, false not.\n\t * @return An object with data of last row, or null.\n\t */\n    public <T> T onFindLast(Class<T> modelClass, boolean isEager) {\n\t\tList<T> dataList = query(modelClass, null, null, null, null, null, \"id desc\", \"1\",\n\t\t\t\tgetForeignKeyAssociations(modelClass.getName(), isEager));\n\t\tif (dataList.size() > 0) {\n\t\t\treturn dataList.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to query multiple\n\t * records by an id array. Pass no ids means query all rows.\n\t * \n\t * @param modelClass\n\t *            Which table to query and the object type to return as a list.\n\t * @param isEager\n\t *            True to load the associated models, false not.\n\t * @param ids\n\t *            Which records to query. Or do not pass it to find all records.\n\t * @return An object list with found data from database, or an empty list.\n\t */\n    public <T> List<T> onFindAll(Class<T> modelClass, boolean isEager, long... ids) {\n\t\tList<T> dataList;\n\t\tif (isAffectAllLines(ids)) {\n\t\t\tdataList = query(modelClass, null, null, null, null, null, \"id\", null,\n\t\t\t\t\tgetForeignKeyAssociations(modelClass.getName(), isEager));\n\t\t} else {\n\t\t\tdataList = query(modelClass, null, getWhereOfIdsWithOr(ids), null, null, null, \"id\",\n\t\t\t\t\tnull, getForeignKeyAssociations(modelClass.getName(), isEager));\n\t\t}\n\t\treturn dataList;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to query multiple\n\t * records by parameters.\n\t * \n\t * @param modelClass\n\t *            Which table to query and the object type to return as a list.\n\t * @param columns\n\t *            A String array of which columns to return. Passing null will\n\t *            return all columns.\n\t * @param conditions\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause. Passing null will return all rows.\n\t * @param orderBy\n\t *            How to order the rows, formatted as an SQL ORDER BY clause.\n\t *            Passing null will use the default sort order, which may be\n\t *            unordered.\n\t * @param limit\n\t *            Limits the number of rows returned by the query, formatted as\n\t *            LIMIT clause.\n\t * @param isEager\n\t *            True to load the associated models, false not.\n\t * @return An object list with found data from database, or an empty list.\n\t */\n\tpublic <T> List<T> onFind(Class<T> modelClass, String[] columns, String[] conditions, String orderBy,\n\t\t\tString limit, boolean isEager) {\n\t\tBaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n        orderBy = DBUtility.convertOrderByClauseToValidName(orderBy);\n\t\treturn query(modelClass, columns, getWhereClause(conditions),\n                getWhereArgs(conditions), null, null, orderBy, limit,\n                getForeignKeyAssociations(modelClass.getName(), isEager));\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to Count the\n\t * records.\n\t * \n\t * @param tableName\n\t *            Which table to query from.\n\t * @param conditions\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause. Passing null will return all rows.\n\t * @return Count of the specified table.\n\t */\n    public int onCount(String tableName, String[] conditions) {\n        BaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n\t\treturn mathQuery(tableName, new String[] { \"count(1)\" }, conditions, int.class);\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to calculate the\n\t * average value on a given column.\n\t * \n\t * @param tableName\n\t *            Which table to query from.\n\t * @param column\n\t *            The based on column to calculate.\n\t * @param conditions\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause. Passing null will return all rows.\n\t * @return The average value on a given column.\n\t */\n    public double onAverage(String tableName, String column, String[] conditions) {\n        BaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n\t\treturn mathQuery(tableName, new String[] { \"avg(\" + column + \")\" }, conditions,\n\t\t\t\tdouble.class);\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to calculate the\n\t * maximum value on a given column.\n\t * \n\t * @param tableName\n\t *            Which table to query from.\n\t * @param column\n\t *            The based on column to calculate.\n\t * @param conditions\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause. Passing null will return all rows.\n\t * @param type\n\t *            The type of the based on column.\n\t * @return The maximum value on a given column.\n\t */\n    public <T> T onMax(String tableName, String column, String[] conditions, Class<T> type) {\n        BaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n\t\treturn mathQuery(tableName, new String[] { \"max(\" + column + \")\" }, conditions, type);\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to calculate the\n\t * minimum value on a given column.\n\t * \n\t * @param tableName\n\t *            Which table to query from.\n\t * @param column\n\t *            The based on column to calculate.\n\t * @param conditions\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause. Passing null will return all rows.\n\t * @param type\n\t *            The type of the based on column.\n\t * @return The minimum value on a given column.\n\t */\n    public <T> T onMin(String tableName, String column, String[] conditions, Class<T> type) {\n        BaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n\t\treturn mathQuery(tableName, new String[] { \"min(\" + column + \")\" }, conditions, type);\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to calculate the sum\n\t * of values on a given column.\n\t * \n\t * @param tableName\n\t *            Which table to query from.\n\t * @param column\n\t *            The based on column to calculate.\n\t * @param conditions\n\t *            A filter declaring which rows to return, formatted as an SQL\n\t *            WHERE clause. Passing null will return all rows.\n\t * @param type\n\t *            The type of the based on column.\n\t * @return The sum value on a given column.\n\t */\n    public <T> T onSum(String tableName, String column, String[] conditions, Class<T> type) {\n        BaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n\t\treturn mathQuery(tableName, new String[] { \"sum(\" + column + \")\" }, conditions, type);\n\t}\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/SaveHandler.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport android.content.ContentValues;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.util.Log;\n\nimport org.litepal.annotation.Encrypt;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.util.DBUtility;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static org.litepal.util.BaseUtility.changeCase;\n\n/**\n * This is a component under LitePalSupport. It deals with the saving stuff as\n * primary task. All the implementation based on the java reflection API and\n * Android SQLiteDatabase API. It will persist the model class into table. If\n * there're some associated models already persisted, it will build the\n * associations in database automatically between the current model and the\n * associated models.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class SaveHandler extends DataHandler {\n\n    private ContentValues values;\n\n    /**\n\t * Initialize {@link org.litepal.crud.DataHandler#mDatabase} for operating database. Do not\n\t * allow to create instance of SaveHandler out of CRUD package.\n\t * \n\t * @param db\n\t *            The instance of SQLiteDatabase.\n\t */\n    public SaveHandler(SQLiteDatabase db) {\n        values = new ContentValues();\n\t\tmDatabase = db;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to save a model. It\n\t * is called when a model class calls the save method. First of all, the\n\t * passed in baseObj will be saved into database. Then LitePal will analyze\n\t * the associations. If there're associated models detected, each associated\n\t * model which is persisted will build association with current model in\n\t * database.\n\t * \n\t * @param baseObj\n\t *            Current model to persist.\n\t */\n\tvoid onSave(LitePalSupport baseObj) throws SecurityException, IllegalArgumentException,\n\t\t\tNoSuchMethodException, IllegalAccessException, InvocationTargetException {\n\t\tString className = baseObj.getClassName();\n\t\tList<Field> supportedFields = getSupportedFields(className);\n        List<Field> supportedGenericFields = getSupportedGenericFields(className);\n\t\tCollection<AssociationsInfo> associationInfos = getAssociationInfo(className);\n\t\tif (!baseObj.isSaved()) {\n            analyzeAssociatedModels(baseObj, associationInfos);\n\t\t\tdoSaveAction(baseObj, supportedFields, supportedGenericFields);\n            analyzeAssociatedModels(baseObj, associationInfos);\n\t\t} else {\n            analyzeAssociatedModels(baseObj, associationInfos);\n\t\t\tdoUpdateAction(baseObj, supportedFields, supportedGenericFields);\n\t\t}\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to save a model\n\t * collection. It is called when developer calls\n\t * {@link org.litepal.Operator#saveAll(java.util.Collection)}. Each model in the collection\n\t * will be persisted. If there're associated models detected, each\n\t * associated model which is persisted will build association with current\n\t * model in database.\n\t * \n\t * @param collection\n\t *            Holds all models to persist.\n\t */\n\tpublic <T extends LitePalSupport> void onSaveAll(Collection<T> collection) throws SecurityException,\n\t\t\tIllegalArgumentException, NoSuchMethodException, IllegalAccessException,\n\t\t\tInvocationTargetException {\n\t\tif (collection != null && collection.size() > 0) {\n\t\t\tLitePalSupport[] array = collection.toArray(new LitePalSupport[0]);\n\t\t\tLitePalSupport firstObj = array[0];\n\t\t\tString className = firstObj.getClassName();\n\t\t\tList<Field> supportedFields = getSupportedFields(className);\n            List<Field> supportedGenericFields = getSupportedGenericFields(className);\n\t\t\tCollection<AssociationsInfo> associationInfos = getAssociationInfo(className);\n\t\t\tfor (LitePalSupport baseObj : array) {\n\t\t\t\tif (!baseObj.isSaved()) {\n\t\t\t\t\tanalyzeAssociatedModels(baseObj, associationInfos);\n\t\t\t\t\tdoSaveAction(baseObj, supportedFields, supportedGenericFields);\n\t\t\t\t\tanalyzeAssociatedModels(baseObj, associationInfos);\n\t\t\t\t} else {\n\t\t\t\t\tanalyzeAssociatedModels(baseObj, associationInfos);\n\t\t\t\t\tdoUpdateAction(baseObj, supportedFields, supportedGenericFields);\n\t\t\t\t}\n\t\t\t\tbaseObj.clearAssociatedData();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Persisting model class into database happens here. But first\n\t * {@link #beforeSave(LitePalSupport, java.util.List, android.content.ContentValues)} will be called to\n\t * put the values for ContentValues. When the model is saved,\n\t * {@link #afterSave(LitePalSupport, java.util.List, java.util.List, long)} will be called to do stuffs\n\t * after model is saved. Note that SaveSupport won't help with id. Any\n\t * developer who wants to set value to id will be ignored here. The value of\n\t * id will be generated by SQLite automatically.\n\t * \n\t * @param baseObj\n\t *            Current model to persist.\n\t * @param supportedFields\n\t *            List of all supported fields.\n     * @param  supportedGenericFields\n     *            List of all supported generic fields.\n\t */\n\tprivate void doSaveAction(LitePalSupport baseObj, List<Field> supportedFields, List<Field> supportedGenericFields)\n\t\t\tthrows SecurityException, IllegalArgumentException, NoSuchMethodException,\n\t\t\tIllegalAccessException, InvocationTargetException {\n\t\tvalues.clear();\n\t\tbeforeSave(baseObj, supportedFields, values);\n\t\tlong id = saving(baseObj, values);\n\t\tafterSave(baseObj, supportedFields, supportedGenericFields, id);\n\t}\n\n\t/**\n\t * Before the self model is saved, it will be analyzed first. Put all the\n\t * data contained by the model into ContentValues, including the fields\n\t * value and foreign key value.\n\t * \n\t * @param baseObj\n\t *            Current model to persist.\n\t * @param supportedFields\n\t *            List of all supported fields.\n\t * @param values\n\t *            To store data of current model for persisting.\n\t */\n\tprivate void beforeSave(LitePalSupport baseObj, List<Field> supportedFields, ContentValues values)\n\t\t\tthrows SecurityException, IllegalArgumentException, NoSuchMethodException,\n\t\t\tIllegalAccessException, InvocationTargetException {\n\t\tputFieldsValue(baseObj, supportedFields, values);\n        putForeignKeyValue(values, baseObj);\n\t}\n\n\t/**\n\t * Calling {@link android.database.sqlite.SQLiteDatabase#insert(String, String, android.content.ContentValues)} to\n\t * persist the current model.\n\t * \n\t * @param baseObj\n\t *            Current model to persist.\n\t * @param values\n\t *            To store data of current model for persisting.\n\t * @return The row ID of the newly inserted row, or -1 if an error occurred.\n\t */\n\tprivate long saving(LitePalSupport baseObj, ContentValues values) {\n        if (values.size() == 0) {\n            values.putNull(\"id\");\n        }\n\t\treturn mDatabase.insert(baseObj.getTableName(), null, values);\n\t}\n\n\t/**\n\t * After the model is saved, do the extra work that need to do.\n\t * \n\t * @param baseObj\n\t *            Current model that is persisted.\n\t * @param supportedFields\n\t *            List of all supported fields.\n     * @param  supportedGenericFields\n     *            List of all supported generic fields.\n\t * @param id\n\t *            The current model's id.\n\t */\n\tprivate void afterSave(LitePalSupport baseObj, List<Field> supportedFields,\n                           List<Field> supportedGenericFields, long id) throws IllegalAccessException, InvocationTargetException {\n\t\tthrowIfSaveFailed(id);\n\t\tassignIdValue(baseObj, getIdField(supportedFields), id);\n        updateGenericTables(baseObj, supportedGenericFields, id);\n        updateAssociatedTableWithFK(baseObj);\n        insertIntermediateJoinTableValue(baseObj, false);\n\t}\n\n\t/**\n\t * When a model is associated with two different models.\n\t * \n\t * @param baseObj\n\t *            The class of base object.\n     * @param supportedFields\n     *            List of all supported fields.\n     * @param  supportedGenericFields\n     *            List of all supported generic fields.\n\t */\n\tprivate void doUpdateAction(LitePalSupport baseObj, List<Field> supportedFields, List<Field> supportedGenericFields)\n\t\t\tthrows SecurityException, IllegalArgumentException, NoSuchMethodException,\n\t\t\tIllegalAccessException, InvocationTargetException {\n\t\tvalues.clear();\n\t\tbeforeUpdate(baseObj, supportedFields, values);\n\t\tupdating(baseObj, values);\n\t\tafterUpdate(baseObj, supportedGenericFields);\n\t}\n\n\t/**\n\t * Before updating model, it will be analyzed first. Put all the data\n\t * contained by the model into ContentValues, including the fields value and\n\t * foreign key value. If the associations between models has been removed.\n\t * The foreign key value in database should be cleared too.\n\t * \n\t * @param baseObj\n\t *            Current model to update.\n\t * @param supportedFields\n\t *            List of all supported fields.\n\t * @param values\n\t *            To store data of current model for updating.\n\t */\n\tprivate void beforeUpdate(LitePalSupport baseObj, List<Field> supportedFields, ContentValues values)\n\t\t\tthrows SecurityException, IllegalArgumentException, NoSuchMethodException,\n\t\t\tIllegalAccessException, InvocationTargetException {\n\t\tputFieldsValue(baseObj, supportedFields, values);\n        putForeignKeyValue(values, baseObj);\n        for (String fkName : baseObj.getListToClearSelfFK()) {\n            values.putNull(fkName);\n        }\n\t}\n\n\t/**\n\t * Calling\n\t * {@link android.database.sqlite.SQLiteDatabase#update(String, android.content.ContentValues, String, String[])} to\n\t * update the current model.\n\t * \n\t * @param baseObj\n\t *            Current model to update.\n\t * @param values\n\t *            To store data of current model for updating.\n\t */\n\tprivate void updating(LitePalSupport baseObj, ContentValues values) {\n\t    if (values.size() > 0) {\n            mDatabase.update(baseObj.getTableName(), values, \"id = ?\",\n                    new String[] { String.valueOf(baseObj.getBaseObjId()) });\n        }\n\t}\n\n\t/**\n\t * After the model is updated, do the extra work that need to do.\n\t * \n\t * @param baseObj\n\t *            Current model that is updated.\n     * @param  supportedGenericFields\n     *            List of all supported generic fields.\n\t */\n\tprivate void afterUpdate(LitePalSupport baseObj, List<Field> supportedGenericFields)\n            throws InvocationTargetException, IllegalAccessException {\n        updateGenericTables(baseObj, supportedGenericFields, baseObj.getBaseObjId());\n        updateAssociatedTableWithFK(baseObj);\n        insertIntermediateJoinTableValue(baseObj, true);\n        clearFKValueInAssociatedTable(baseObj);\n\t}\n\n\t/**\n\t * Get the id field by the passed in field list.\n\t * \n\t * @param supportedFields\n\t *            The field list to find from.\n\t * @return The id field. If not found one return null.\n\t */\n\tprivate Field getIdField(List<Field> supportedFields) {\n\t\tfor (Field field : supportedFields) {\n\t\t\tif (isIdColumn(field.getName())) {\n\t\t\t\treturn field;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * If the model saved failed, throw an exception.\n\t * \n\t * @param id\n\t *            The id returned by SQLite. -1 means saved failed.\n\t */\n\tprivate void throwIfSaveFailed(long id) {\n\t\tif (id == -1) {\n\t\t\tthrow new LitePalSupportException(LitePalSupportException.SAVE_FAILED);\n\t\t}\n\t}\n\n\t/**\n\t * Assign the generated id value to the model. The\n\t * {@link LitePalSupport#baseObjId} will be assigned anyway. If the model has a\n\t * field named id or _id, LitePal will assign it too. The\n\t * {@link LitePalSupport#baseObjId} will be used as identify of this model for\n\t * system use. The id or _id field will help developers for their own\n\t * purpose.\n\t * \n\t * @param baseObj\n\t *            Current model that is persisted.\n\t * @param idField\n\t *            The field of id.\n\t * @param id\n\t *            The value of id.\n\t */\n\tprivate void assignIdValue(LitePalSupport baseObj, Field idField, long id) {\n\t\ttry {\n\t\t\tgiveBaseObjIdValue(baseObj, id);\n\t\t\tif (idField != null) {\n\t\t\t\tgiveModelIdValue(baseObj, idField.getName(), idField.getType(), id);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * After saving a model, the id for this model will be returned. Assign this\n\t * id to the model's id or _id field if it exists.\n\t * \n\t * @param baseObj\n\t *            The class of base object.\n\t * @param idName\n\t *            The name of id. Only id or _id is valid.\n\t * @param idType\n\t *            The type of id. Only int or long is valid.\n\t * @param id\n\t *            The value of id.\n\t */\n\tprivate void giveModelIdValue(LitePalSupport baseObj, String idName, Class<?> idType, long id)\n\t\t\tthrows SecurityException, IllegalArgumentException,\n\t\t\tIllegalAccessException {\n\t\tif (shouldGiveModelIdValue(idName, idType, id)) {\n\t\t\tObject value;\n\t\t\tif (idType == int.class || idType == Integer.class) {\n\t\t\t\tvalue = (int) id;\n\t\t\t} else if (idType == long.class || idType == Long.class) {\n\t\t\t\tvalue = id;\n\t\t\t} else {\n\t\t\t\tthrow new LitePalSupportException(LitePalSupportException.ID_TYPE_INVALID_EXCEPTION);\n\t\t\t}\n\t\t\tDynamicExecutor.setField(baseObj, idName, value, baseObj.getClass());\n\t\t}\n\t}\n\n\t/**\n\t * If the table for this model have a foreign key column, the value of\n\t * foreign key id should be saved too.\n\t * \n\t * @param values\n\t *            The instance of ContentValues to put foreign key value.\n\t */\n\tprivate void putForeignKeyValue(ContentValues values, LitePalSupport baseObj) {\n\t\tMap<String, Long> associatedModelMap = baseObj.getAssociatedModelsMapWithoutFK();\n\t\tfor (String associatedTableName : associatedModelMap.keySet()) {\n\t\t\tvalues.put(getForeignKeyColumnName(associatedTableName),\n\t\t\t\t\tassociatedModelMap.get(associatedTableName));\n\t\t}\n\t}\n\n\t/**\n\t * Update the foreign keys in the associated model's table.\n\t * \n\t * @param baseObj\n\t *            Current model that is persisted.\n\t */\n\tprivate void updateAssociatedTableWithFK(LitePalSupport baseObj) {\n\t\tMap<String, Set<Long>> associatedModelMap = baseObj.getAssociatedModelsMapWithFK();\n\t\tContentValues values = new ContentValues();\n\t\tfor (String associatedTableName : associatedModelMap.keySet()) {\n\t\t\tvalues.clear();\n\t\t\tString fkName = getForeignKeyColumnName(baseObj.getTableName());\n\t\t\tvalues.put(fkName, baseObj.getBaseObjId());\n\t\t\tSet<Long> ids = associatedModelMap.get(associatedTableName);\n\t\t\tif (ids != null && !ids.isEmpty()) {\n\t\t\t\tmDatabase.update(associatedTableName, values, getWhereOfIdsWithOr(ids), null);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * When the associations breaks between current model and associated models,\n\t * clear all the associated models' foreign key value if it exists.\n\t * \n\t * @param baseObj\n\t *            Current model that is persisted.\n\t */\n\tprivate void clearFKValueInAssociatedTable(LitePalSupport baseObj) {\n\t\tList<String> associatedTableNames = baseObj.getListToClearAssociatedFK();\n\t\tfor (String associatedTableName : associatedTableNames) {\n\t\t\tString fkColumnName = getForeignKeyColumnName(baseObj.getTableName());\n\t\t\tContentValues values = new ContentValues();\n\t\t\tvalues.putNull(fkColumnName);\n\t\t\tString whereClause = fkColumnName + \" = \" + baseObj.getBaseObjId();\n\t\t\tmDatabase.update(associatedTableName, values, whereClause, null);\n\t\t}\n\t}\n\n\t/**\n\t * Insert values into intermediate join tables for self model and associated\n\t * models.\n\t * \n\t * @param baseObj\n\t *            Current model that is persisted.\n\t * @param isUpdate\n\t *            The current action is update or not.\n\t */\n\tprivate void insertIntermediateJoinTableValue(LitePalSupport baseObj, boolean isUpdate) {\n\t\tMap<String, List<Long>> associatedIdsM2M = baseObj.getAssociatedModelsMapForJoinTable();\n\t\tContentValues values = new ContentValues();\n\t\tfor (String associatedTableName : associatedIdsM2M.keySet()) {\n\t\t\tString joinTableName = getIntermediateTableName(baseObj, associatedTableName);\n\t\t\tif (isUpdate) {\n\t\t\t\tmDatabase.delete(joinTableName, getWhereForJoinTableToDelete(baseObj),\n\t\t\t\t\t\tnew String[] { String.valueOf(baseObj.getBaseObjId()) });\n\t\t\t}\n\t\t\tList<Long> associatedIdsM2MSet = associatedIdsM2M.get(associatedTableName);\n\t\t\tif (associatedIdsM2MSet != null) {\n\t\t\t\tfor (long associatedId : associatedIdsM2MSet) {\n\t\t\t\t\tvalues.clear();\n\t\t\t\t\tvalues.put(getForeignKeyColumnName(baseObj.getTableName()), baseObj.getBaseObjId());\n\t\t\t\t\tvalues.put(getForeignKeyColumnName(associatedTableName), associatedId);\n\t\t\t\t\tmDatabase.insert(joinTableName, null, values);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the where clause to delete intermediate join table's value for\n\t * updating.\n\t * \n\t * @param baseObj\n\t *            Current model that is persisted.\n\t * @return The where clause to execute.\n\t */\n\tprivate String getWhereForJoinTableToDelete(LitePalSupport baseObj) {\n\t\tStringBuilder where = new StringBuilder();\n\t\twhere.append(getForeignKeyColumnName(baseObj.getTableName()));\n\t\twhere.append(\" = ?\");\n\t\treturn where.toString();\n\t}\n\n\t/**\n\t * Judge should assign id value to model's id field. The principle is that\n\t * if id name is not null, id type is not null and id is greater than 0,\n\t * then should assign id value to it.\n\t * \n\t * @param idName\n\t *            The name of id field.\n\t * @param idType\n\t *            The type of id field.\n\t * @param id\n\t *            The value of id.\n\t * @return If id name is not null, id type is not null and id is greater\n\t *         than 0, return true. Otherwise return false.\n\t */\n\tprivate boolean shouldGiveModelIdValue(String idName, Class<?> idType, long id) {\n\t\treturn idName != null && idType != null && id > 0;\n\t}\n\n    /**\n     * Update the generic data in generic tables. Need to delete the related generic data before\n     * saving, because generic data has no id.\n     * @param baseObj\n     *          Current model that is persisted.\n     *@param  supportedGenericFields\n     *            List of all supported generic fields.\n     * @param id\n     *          The id of current model.\n     */\n    private void updateGenericTables(LitePalSupport baseObj, List<Field> supportedGenericFields,\n                                     long id) throws IllegalAccessException, InvocationTargetException {\n        for (Field field : supportedGenericFields) {\n            Encrypt annotation = field.getAnnotation(Encrypt.class);\n            String algorithm = null;\n            String genericTypeName = getGenericTypeName(field);\n            if (annotation != null && \"java.lang.String\".equals(genericTypeName)) {\n                algorithm = annotation.algorithm();\n            }\n            field.setAccessible(true);\n            Collection<?> collection = (Collection<?>) field.get(baseObj);\n            if (collection != null) {\n                Log.d(TAG, \"updateGenericTables: class name is \" + baseObj.getClassName() + \" , field name is \" + field.getName() );\n                String tableName = DBUtility.getGenericTableName(baseObj.getClassName(), field.getName());\n                String genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(baseObj.getClassName());\n                mDatabase.delete(tableName, genericValueIdColumnName + \" = ?\", new String[] {String.valueOf(id)});\n                for (Object object : collection) {\n                    ContentValues values = new ContentValues();\n                    values.put(genericValueIdColumnName, id);\n                    object = encryptValue(algorithm, object);\n                    if (baseObj.getClassName().equals(genericTypeName)) {\n                        LitePalSupport dataSupport = (LitePalSupport) object;\n                        if (dataSupport == null) {\n                            continue;\n                        }\n                        long baseObjId = dataSupport.getBaseObjId();\n                        if (baseObjId <= 0) {\n                            continue;\n                        }\n                        values.put(DBUtility.getM2MSelfRefColumnName(field), baseObjId);\n                    } else {\n                        Object[] parameters = new Object[] { changeCase(DBUtility.convertToValidColumnName(field.getName())), object };\n                        Class<?>[] parameterTypes = new Class[] { String.class, getGenericTypeClass(field) };\n                        DynamicExecutor.send(values, \"put\", parameters, values.getClass(), parameterTypes);\n                    }\n                    mDatabase.insert(tableName, null, values);\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/UpdateHandler.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud;\n\nimport android.content.ContentValues;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.os.Build;\n\nimport org.litepal.Operator;\nimport org.litepal.annotation.Encrypt;\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.DBUtility;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static org.litepal.util.BaseUtility.changeCase;\n\n/**\n * This is a component under LitePalSupport. It deals with the updating stuff as\n * primary task. Either updating specifying data with id or updating multiple\n * lines with conditions can be done here.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class UpdateHandler extends DataHandler {\n\n\t/**\n\t * Initialize {@link org.litepal.crud.DataHandler#mDatabase} for operating database. Do not\n\t * allow to create instance of UpdateHandler out of CRUD package.\n\t * \n\t * @param db\n\t *            The instance of SQLiteDatabase.\n\t */\n    public UpdateHandler(SQLiteDatabase db) {\n\t\tmDatabase = db;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to update. Using\n\t * baseObj to decide which table to update, and id to decide a specific row.\n\t * The value that need to update is stored in baseObj.\n\t * \n\t * @param baseObj\n\t *            Which table to update by model instance.\n\t * @param id\n\t *            Which record to update.\n\t * @return The number of rows affected.\n\t * @throws java.lang.reflect.InvocationTargetException\n\t * @throws IllegalAccessException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalArgumentException\n\t * @throws SecurityException\n\t */\n\tint onUpdate(LitePalSupport baseObj, long id) throws SecurityException, IllegalArgumentException,\n\t\t\tNoSuchMethodException, IllegalAccessException, InvocationTargetException {\n\t\tList<Field> supportedFields = getSupportedFields(baseObj.getClassName());\n\t\tList<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());\n        updateGenericTables(baseObj, supportedGenericFields, id);\n\t\tContentValues values = new ContentValues();\n\t\tputFieldsValue(baseObj, supportedFields, values);\n\t\tputFieldsToDefaultValue(baseObj, values, id);\n\t\tif (values.size() > 0) {\n\t\t\treturn mDatabase.update(baseObj.getTableName(), values, \"id = \" + id, null);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to update. Using\n\t * modelClass to decide which table to update, and id to decide a specific\n\t * row. The value that need to update is stored in ContentValues.\n\t * \n\t * @param modelClass\n\t *            Which table to update by class.\n\t * @param id\n\t *            Which record to update.\n\t * @param values\n\t *            A map from column names to new column values. null is a valid\n\t *            value that will be translated to NULL.\n\t * @return The number of rows affected.\n\t */\n    public int onUpdate(Class<?> modelClass, long id, ContentValues values) {\n\t\tif (values.size() > 0) {\n            convertContentValues(values);\n            return mDatabase.update(getTableName(modelClass), values, \"id = \" + id, null);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to update multiple\n\t * rows. Using baseObj to decide which table to update, and conditions\n\t * representing the WHERE part of an SQL statement. The value that need to\n\t * update is stored in baseObj.\n\t * \n\t * @param baseObj\n\t *            Which table to update by model instance.\n\t * @param conditions\n\t *            A string array representing the WHERE part of an SQL\n\t *            statement.\n\t * @return The number of rows affected.\n\t * @throws java.lang.reflect.InvocationTargetException\n\t * @throws IllegalAccessException\n\t * @throws NoSuchMethodException\n\t * @throws IllegalArgumentException\n\t * @throws SecurityException\n\t */\n    @SuppressWarnings(\"unchecked\")\n\tint onUpdateAll(LitePalSupport baseObj, String... conditions) throws SecurityException,\n\t\t\tIllegalArgumentException, NoSuchMethodException, IllegalAccessException,\n\t\t\tInvocationTargetException {\n        BaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n\t\tList<Field> supportedFields = getSupportedFields(baseObj.getClassName());\n        List<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());\n        long[] ids = null;\n        if (!supportedGenericFields.isEmpty()) {\n            List<LitePalSupport> list = (List<LitePalSupport>) Operator.select(\"id\").where(conditions).find(baseObj.getClass());\n            if (list.size() > 0) {\n                ids = new long[list.size()];\n                for (int i = 0; i < ids.length; i++) {\n                    LitePalSupport dataSupport = list.get(i);\n                    ids[i] = dataSupport.getBaseObjId();\n                }\n                updateGenericTables(baseObj, supportedGenericFields, ids);\n            }\n        }\n        ContentValues values = new ContentValues();\n\t\tputFieldsValue(baseObj, supportedFields, values);\n\t\tputFieldsToDefaultValue(baseObj, values, ids);\n\t\treturn doUpdateAllAction(baseObj.getTableName(), values, conditions);\n\t}\n\n\t/**\n\t * The open interface for other classes in CRUD package to update multiple\n\t * rows. Using tableName to decide which table to update, and conditions\n\t * representing the WHERE part of an SQL statement. The value that need to\n\t * update is stored in ContentValues.\n\t * \n\t * @param tableName\n\t *            Which table to update.\n\t * @param conditions\n\t *            A string array representing the WHERE part of an SQL\n\t *            statement.\n\t * @param values\n\t *            A map from column names to new column values. null is a valid\n\t *            value that will be translated to NULL.\n\t * @return The number of rows affected.\n\t */\n    public int onUpdateAll(String tableName, ContentValues values, String... conditions) {\n        BaseUtility.checkConditionsCorrect(conditions);\n        if (conditions != null && conditions.length > 0) {\n            conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);\n        }\n        convertContentValues(values);\n\t\treturn doUpdateAllAction(tableName, values, conditions);\n\t}\n\n\t/**\n\t * Do the action for updating multiple rows. It will check the validity of\n\t * conditions, then update rows in database. If the format of conditions is\n\t * invalid, throw LitePalSupportException.\n\t * \n\t * @param tableName\n\t *            Which table to delete from.\n\t * @param conditions\n\t *            A string array representing the WHERE part of an SQL\n\t *            statement.\n\t * @param values\n\t *            A map from column names to new column values. null is a valid\n\t *            value that will be translated to NULL.\n\t * @return The number of rows affected.\n\t */\n\tprivate int doUpdateAllAction(String tableName, ContentValues values, String... conditions) {\n\t\tBaseUtility.checkConditionsCorrect(conditions);\n\t\tif (values.size() > 0) {\n\t\t\treturn mDatabase.update(tableName, values, getWhereClause(conditions),\n\t\t\t\t\tgetWhereArgs(conditions));\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Iterate all the fields that need to set to default value. If the field is\n\t * id, ignore it. Or put the default value of field into ContentValues.\n\t * \n\t * @param baseObj\n\t *            Which table to update by model instance.\n\t * @param values\n\t *            To store data of current model for persisting or updating.\n     * @param ids\n     *          The id array of query result.\n\t */\n\tprivate void putFieldsToDefaultValue(LitePalSupport baseObj, ContentValues values, long... ids) {\n\t\tString fieldName = null;\n\t\ttry {\n\t\t\tLitePalSupport emptyModel = getEmptyModel(baseObj);\n\t\t\tClass<?> emptyModelClass = emptyModel.getClass();\n\t\t\tfor (String name : baseObj.getFieldsToSetToDefault()) {\n\t\t\t\tif (!isIdColumn(name)) {\n\t\t\t\t\tfieldName = name;\n\t\t\t\t\tField field = emptyModelClass.getDeclaredField(fieldName);\n                    if (isCollection(field.getType())) {\n                        if (ids != null && ids.length > 0) {\n                            String genericTypeName = getGenericTypeName(field);\n                            if (BaseUtility.isGenericTypeSupported(genericTypeName)) {\n                                String tableName = DBUtility.getGenericTableName(baseObj.getClassName(), field.getName());\n                                String genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(baseObj.getClassName());\n                                StringBuilder whereClause = new StringBuilder();\n                                boolean needOr = false;\n                                for (long id : ids) {\n                                    if (needOr) {\n                                        whereClause.append(\" or \");\n                                    }\n                                    whereClause.append(genericValueIdColumnName).append(\" = \").append(id);\n                                    needOr = true;\n                                }\n                                mDatabase.delete(tableName, whereClause.toString(), null);\n                            }\n                        }\n                    } else {\n\t\t\t\t\t    putContentValuesForUpdate(emptyModel, field, values);\n                    }\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (NoSuchFieldException e) {\n\t\t\tthrow new LitePalSupportException(LitePalSupportException.noSuchFieldExceptioin(\n\t\t\t\t\tbaseObj.getClassName(), fieldName), e);\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Unused currently.\n\t */\n\t@SuppressWarnings(\"unused\")\n\tprivate int doUpdateAssociations(LitePalSupport baseObj, long id, ContentValues values) {\n\t\tint rowsAffected = 0;\n\t\tanalyzeAssociations(baseObj);\n\t\tupdateSelfTableForeignKey(baseObj, values);\n\t\trowsAffected += updateAssociatedTableForeignKey(baseObj, id);\n\t\treturn rowsAffected;\n\t}\n\n\t/**\n\t * Analyze the associations of baseObj and store the result in it. The\n\t * associations will be used when deleting referenced data of baseObj.\n\t * Unused currently.\n\t * \n\t * @param baseObj\n\t *            The record to update.\n\t */\n\tprivate void analyzeAssociations(LitePalSupport baseObj) {\n\t\ttry {\n\t\t\tCollection<AssociationsInfo> associationInfos = getAssociationInfo(baseObj\n\t\t\t\t\t.getClassName());\n\t\t\tanalyzeAssociatedModels(baseObj, associationInfos);\n\t\t} catch (Exception e) {\n\t\t\tthrow new LitePalSupportException(e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Unused currently.\n\t */\n\tprivate void updateSelfTableForeignKey(LitePalSupport baseObj, ContentValues values) {\n\t\tMap<String, Long> associatedModelMap = baseObj.getAssociatedModelsMapWithoutFK();\n\t\tfor (String associatedTable : associatedModelMap.keySet()) {\n\t\t\tString fkName = getForeignKeyColumnName(associatedTable);\n\t\t\tvalues.put(fkName, associatedModelMap.get(associatedTable));\n\t\t}\n\t}\n\n\t/**\n\t * Unused currently.\n\t */\n\tprivate int updateAssociatedTableForeignKey(LitePalSupport baseObj, long id) {\n\t\tMap<String, Set<Long>> associatedModelMap = baseObj.getAssociatedModelsMapWithFK();\n\t\tContentValues values = new ContentValues();\n\t\tfor (String associatedTable : associatedModelMap.keySet()) {\n\t\t\tvalues.clear();\n\t\t\tString fkName = getForeignKeyColumnName(baseObj.getTableName());\n\t\t\tvalues.put(fkName, id);\n\t\t\tSet<Long> ids = associatedModelMap.get(associatedTable);\n\t\t\tif (ids != null && !ids.isEmpty()) {\n\t\t\t\treturn mDatabase.update(associatedTable, values, getWhereOfIdsWithOr(ids), null);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n    /**\n     * Update the generic data in generic tables. Need to delete the related generic data before\n     * saving, because generic data has no id. If generic collection is null or empty, the operation\n     * will be abort. Clear generic collection data while updating should use {@link LitePalSupport#setToDefault(String)}\n     * method.\n     * @param baseObj\n     *          Current model that is persisted.\n     *@param  supportedGenericFields\n     *            List of all supported generic fields.\n     * @param ids\n     *          The id array of models.\n     * @throws IllegalAccessException\n     * @throws InvocationTargetException\n     */\n    private void updateGenericTables(LitePalSupport baseObj, List<Field> supportedGenericFields,\n                                     long... ids) throws IllegalAccessException, InvocationTargetException {\n        if (ids != null && ids.length > 0) {\n            for (Field field : supportedGenericFields) {\n                Encrypt annotation = field.getAnnotation(Encrypt.class);\n                String algorithm = null;\n                String genericTypeName = getGenericTypeName(field);\n                if (annotation != null && \"java.lang.String\".equals(genericTypeName)) {\n                    algorithm = annotation.algorithm();\n                }\n                field.setAccessible(true);\n                Collection<?> collection = (Collection<?>) field.get(baseObj);\n                if (collection != null && !collection.isEmpty()) {\n                    String tableName = DBUtility.getGenericTableName(baseObj.getClassName(), field.getName());\n                    String genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(baseObj.getClassName());\n                    for (long id : ids) {\n                        mDatabase.delete(tableName, genericValueIdColumnName + \" = ?\", new String[] {String.valueOf(id)});\n                        for (Object object : collection) {\n                            ContentValues values = new ContentValues();\n                            values.put(genericValueIdColumnName, id);\n                            object = encryptValue(algorithm, object);\n                            if (baseObj.getClassName().equals(genericTypeName)) {\n                                LitePalSupport dataSupport = (LitePalSupport) object;\n                                if (dataSupport == null) {\n                                    continue;\n                                }\n                                long baseObjId = dataSupport.getBaseObjId();\n                                if (baseObjId <= 0) {\n                                    continue;\n                                }\n                                values.put(DBUtility.getM2MSelfRefColumnName(field), baseObjId);\n                            } else {\n                                Object[] parameters = new Object[] { DBUtility.convertToValidColumnName(changeCase(field.getName())), object };\n                                Class<?>[] parameterTypes = new Class[] { String.class, getGenericTypeClass(field) };\n                                DynamicExecutor.send(values, \"put\", parameters, values.getClass(), parameterTypes);\n                            }\n                            mDatabase.insert(tableName, null, values);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * The keys in ContentValues may be put as valid in Java but invalid in database. So convert\n     * them into valid keys.\n     * @param values\n     *          A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     */\n    private void convertContentValues(ContentValues values) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            Map<String, Object> valuesToConvert = new HashMap<String, Object>();\n            for (String key : values.keySet()) {\n                if (DBUtility.isFieldNameConflictWithSQLiteKeywords(key)) {\n                    Object object = values.get(key);\n                    valuesToConvert.put(key, object);\n                }\n            }\n            for (String key : valuesToConvert.keySet()) {\n                String convertedKey = DBUtility.convertToValidColumnName(key);\n                Object object = values.get(key);\n                values.remove(key);\n                if (object == null) {\n                    values.putNull(convertedKey);\n                } else {\n                    String className = object.getClass().getName();\n                    if (\"java.lang.Byte\".equals(className)) {\n                        values.put(convertedKey, (Byte) object);\n                    } else if (\"[B\".equals(className)) {\n                        values.put(convertedKey, (byte[]) object);\n                    } else if (\"java.lang.Boolean\".equals(className)) {\n                        values.put(convertedKey, (Boolean) object);\n                    } else if (\"java.lang.String\".equals(className)) {\n                        values.put(convertedKey, (String) object);\n                    } else if (\"java.lang.Float\".equals(className)) {\n                        values.put(convertedKey, (Float) object);\n                    } else if (\"java.lang.Long\".equals(className)) {\n                        values.put(convertedKey, (Long) object);\n                    } else if (\"java.lang.Integer\".equals(className)) {\n                        values.put(convertedKey, (Integer) object);\n                    } else if (\"java.lang.Short\".equals(className)) {\n                        values.put(convertedKey, (Short) object);\n                    } else if (\"java.lang.Double\".equals(className)) {\n                        values.put(convertedKey, (Double) object);\n                    }\n                }\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/async/AsyncExecutor.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.async;\n\n/**\n * A simple async executor to run tasks in background thread.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic abstract class AsyncExecutor {\n\n    /**\n     * Task that pending to run.\n     */\n    private Runnable pendingTask;\n\n    /**\n     * Submit a task for pending executing.\n     * @param task\n     *          The task with specific database operation.\n     */\n    public void submit(Runnable task) {\n        pendingTask = task;\n    }\n\n    /**\n     * Run the pending task in background thread.\n     */\n    void execute() {\n        if (pendingTask != null) {\n            new Thread(pendingTask).start();\n        }\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/async/AverageExecutor.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.async;\n\nimport org.litepal.crud.callback.AverageCallback;\n\n/**\n * Executor for average query in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic class AverageExecutor extends AsyncExecutor {\n\n    private AverageCallback cb;\n\n    /**\n     * Register a callback listener and async task will start executing right away.\n     * @param callback\n     *          Callback for average query in background.\n     */\n    public void listen(AverageCallback callback) {\n        cb = callback;\n        execute();\n    }\n\n    public AverageCallback getListener() {\n        return  cb;\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/async/CountExecutor.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.async;\n\nimport org.litepal.crud.callback.CountCallback;\n\n/**\n * Executor for count query in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic class CountExecutor extends AsyncExecutor {\n\n    private CountCallback cb;\n\n    /**\n     * Register a callback listener and async task will start executing right away.\n     * @param callback\n     *          Callback for count query in background.\n     */\n    public void listen(CountCallback callback) {\n        cb = callback;\n        execute();\n    }\n\n    public CountCallback getListener() {\n        return  cb;\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/async/FindExecutor.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.async;\n\nimport org.litepal.crud.callback.FindCallback;\n\n/**\n * Executor for find record in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic class FindExecutor<T> extends AsyncExecutor {\n\n    private FindCallback<T> cb;\n\n    /**\n     * Register a callback listener and async task will start executing right away.\n     * @param callback\n     *          Callback for find record in background.\n     */\n    public void listen(FindCallback<T> callback) {\n        cb = callback;\n        execute();\n    }\n\n    public FindCallback<T> getListener() {\n        return  cb;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/async/FindMultiExecutor.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.async;\n\nimport org.litepal.crud.callback.FindMultiCallback;\n\n/**\n * Executor for find multiple records in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic class FindMultiExecutor<T> extends AsyncExecutor {\n\n    private FindMultiCallback<T> cb;\n\n    /**\n     * Register a callback listener and async task will start executing right away.\n     * @param callback\n     *          Callback for find multiple records in background.\n     */\n    public void listen(FindMultiCallback<T> callback) {\n        cb = callback;\n        execute();\n    }\n\n    public FindMultiCallback<T> getListener() {\n        return  cb;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/async/SaveExecutor.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.async;\n\nimport org.litepal.crud.callback.SaveCallback;\n\n/**\n * Executor for save records in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic class SaveExecutor extends AsyncExecutor {\n\n    private SaveCallback cb;\n\n    /**\n     * Register a callback listener and async task will start executing right away.\n     * @param callback\n     *          Callback for save records in background.\n     */\n    public void listen(SaveCallback callback) {\n        cb = callback;\n        execute();\n    }\n\n    public SaveCallback getListener() {\n        return  cb;\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/async/UpdateOrDeleteExecutor.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.async;\n\nimport org.litepal.crud.callback.UpdateOrDeleteCallback;\n\n/**\n * Executor for update or delete records in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic class UpdateOrDeleteExecutor extends AsyncExecutor {\n\n    private UpdateOrDeleteCallback cb;\n\n    /**\n     * Register a callback listener and async task will start executing right away.\n     * @param callback\n     *          Callback for update or delete records in background.\n     */\n    public void listen(UpdateOrDeleteCallback callback) {\n        cb = callback;\n        execute();\n    }\n\n    public UpdateOrDeleteCallback getListener() {\n        return  cb;\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/callback/AverageCallback.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.callback;\n\n/**\n * Callback for average query in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic interface AverageCallback {\n\n    void onFinish(double average);\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/callback/CountCallback.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.callback;\n\n/**\n * Callback for count query in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic interface CountCallback {\n\n    void onFinish(int count);\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/callback/FindCallback.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.callback;\n\n/**\n * Callback for find record in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic interface FindCallback<T> {\n\n    void onFinish(T t);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/callback/FindMultiCallback.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.callback;\n\nimport java.util.List;\n\n/**\n * Callback for find multiple records in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic interface FindMultiCallback<T> {\n\n    void onFinish(List<T> list);\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/callback/SaveCallback.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.callback;\n\n/**\n * Callback for save records in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic interface SaveCallback {\n\n    void onFinish(boolean success);\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/callback/UpdateOrDeleteCallback.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.litepal.crud.callback;\n\n/**\n * Callback for update or delete records in background.\n *\n * @author Tony Green\n * @since 2017/2/22\n */\npublic interface UpdateOrDeleteCallback {\n\n    void onFinish(int rowsAffected);\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/crud/model/AssociationsInfo.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.crud.model;\n\nimport java.lang.reflect.Field;\n\n/**\n * This model holds necessary information when comes to analyze and handle\n * associated models of self model.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class AssociationsInfo {\n\n\t/**\n\t * The class name of self class.\n\t */\n\tprivate String selfClassName;\n\n\t/**\n\t * The class name of the class which associated with self class.\n\t */\n\tprivate String associatedClassName;\n\n\t/**\n\t * The class which holds foreign key.\n\t */\n\tprivate String classHoldsForeignKey;\n\n\t/**\n\t * The field of self class to declare has association with other class.\n\t */\n\tprivate Field associateOtherModelFromSelf;\n\n\t/**\n\t * The field of the associated class to declare has association with self\n\t * class.\n\t */\n\tprivate Field associateSelfFromOtherModel;\n\n\t/**\n\t * The association type, including Many2One One2One Many2Many.\n\t */\n\tprivate int associationType;\n\n\t/**\n\t * Get the class name of self class.\n\t * \n\t * @return The self class name.\n\t */\n\tpublic String getSelfClassName() {\n\t\treturn selfClassName;\n\t}\n\n\t/**\n\t * Set the class name of self class.\n\t * \n\t * @param selfClassName\n\t *            The self class name to set.\n\t */\n\tpublic void setSelfClassName(String selfClassName) {\n\t\tthis.selfClassName = selfClassName;\n\t}\n\n\t/**\n\t * Get the class name of the class which associated with self class.\n\t * \n\t * @return The associated class name.\n\t */\n\tpublic String getAssociatedClassName() {\n\t\treturn associatedClassName;\n\t}\n\n\t/**\n\t * Set the class name of the class which associated with self class.\n\t * \n\t * @param associatedClassName\n\t *            The associated class name to set.\n\t */\n\tpublic void setAssociatedClassName(String associatedClassName) {\n\t\tthis.associatedClassName = associatedClassName;\n\t}\n\n\t/**\n\t * Get the class which holds foreign key.\n\t * \n\t * @return The class which holds foreign key.\n\t */\n\tpublic String getClassHoldsForeignKey() {\n\t\treturn classHoldsForeignKey;\n\t}\n\n\t/**\n\t * Set the class which holds foreign key.\n\t * \n\t * @param classHoldsForeignKey\n\t *            The class which holds foreign key to set.\n\t */\n\tpublic void setClassHoldsForeignKey(String classHoldsForeignKey) {\n\t\tthis.classHoldsForeignKey = classHoldsForeignKey;\n\t}\n\n\t/**\n\t * Get the field of self class which declares has association with other\n\t * class.\n\t * \n\t * @return The field which declares has association with other class.\n\t */\n\tpublic Field getAssociateOtherModelFromSelf() {\n\t\treturn associateOtherModelFromSelf;\n\t}\n\n\t/**\n\t * Set the field of self class which declares has association with other\n\t * class.\n\t * \n\t * @param associateOtherModelFromSelf\n\t *            The field which declares has association with other class to\n\t *            set.\n\t */\n\tpublic void setAssociateOtherModelFromSelf(Field associateOtherModelFromSelf) {\n\t\tthis.associateOtherModelFromSelf = associateOtherModelFromSelf;\n\t}\n\n\t/**\n\t * Get the field of the associated class which declares has association with\n\t * self class.\n\t * \n\t * @return The field of the associated class which declares has association\n\t *         with self class.\n\t */\n\tpublic Field getAssociateSelfFromOtherModel() {\n\t\treturn associateSelfFromOtherModel;\n\t}\n\n\t/**\n\t * Set the field of the associated class which declares has association with\n\t * self class.\n\t * \n\t * @param associateSelfFromOtherModel\n\t *            The field of the associated class which declares has\n\t *            association with self class to set.\n\t */\n\tpublic void setAssociateSelfFromOtherModel(Field associateSelfFromOtherModel) {\n\t\tthis.associateSelfFromOtherModel = associateSelfFromOtherModel;\n\t}\n\n\t/**\n\t * Get the association type.\n\t * \n\t * @return The association type.\n\t */\n\tpublic int getAssociationType() {\n\t\treturn associationType;\n\t}\n\n\t/**\n\t * Set the association type.\n\t * \n\t * @param associationType\n\t *            Within ONE_TO_ONE, MANY_TO_ONE and MANY_TO_MANY constants.\n\t */\n\tpublic void setAssociationType(int associationType) {\n\t\tthis.associationType = associationType;\n\t}\n\n\t/**\n\t * Override equals method to make sure that if two associated classes in the\n\t * association info model are same ignoring sides, they are same association\n\t * info model.\n\t */\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o instanceof AssociationsInfo) {\n\t\t\tAssociationsInfo other = (AssociationsInfo) o;\n\t\t\tif (o != null && other != null) {\n\t\t\t\tif (other.getAssociationType() == associationType\n\t\t\t\t\t\t&& other.getClassHoldsForeignKey().equals(classHoldsForeignKey)) {\n\t\t\t\t\tif (other.getSelfClassName().equals(selfClassName)\n\t\t\t\t\t\t\t&& other.getAssociatedClassName().equals(associatedClassName)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (other.getSelfClassName().equals(associatedClassName)\n\t\t\t\t\t\t\t&& other.getAssociatedClassName().equals(selfClassName)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/exceptions/DataSupportException.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.exceptions;\n\n/**\n * When LitePal deals with CRUD actions of LitePalSupport, it may throw\n * DataSupportException for older versions API. The new CRUD APIs should throw\n * {@link LitePalSupportException}\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class DataSupportException extends RuntimeException {\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Constructor of LitePalSupportException.\n\t *\n\t * @param errorMessage\n\t *            the description of this exception.\n\t */\n\tpublic DataSupportException(String errorMessage) {\n\t\tsuper(errorMessage);\n\t}\n\n    /**\n     * Constructor of LitePalSupportException.\n     *\n     * @param errorMessage\n     *            the description of this exception.\n     * @param throwable\n     *            the cause of this exception.\n     */\n    public DataSupportException(String errorMessage, Throwable throwable) {\n        super(errorMessage, throwable);\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/exceptions/DatabaseGenerateException.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.exceptions;\n\n/**\n * When LitePal generate or update tables, it may throw DatabaseGenerateException.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class DatabaseGenerateException extends RuntimeException {\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Can not find a class with the passing class name.\n\t */\n\tpublic static final String CLASS_NOT_FOUND = \"can not find a class named \";\n\n\t/**\n\t * An exception that indicates there was an error with SQL parsing or\n\t * execution.\n\t */\n\tpublic static final String SQL_ERROR = \"An exception that indicates there was an error with SQL parsing or execution. \";\n\n\t/**\n\t * SQL syntax error when executing generation job.\n\t */\n\tpublic static final String SQL_SYNTAX_ERROR = \"SQL syntax error happens while executing \";\n\n\t/**\n\t * Can not find a table with the passing table name when executing SQL.\n\t */\n\tpublic static final String TABLE_DOES_NOT_EXIST_WHEN_EXECUTING = \"Table doesn't exist when executing \";\n\n\t/**\n\t * Can not find a table with the passing table name.\n\t */\n\tpublic static final String TABLE_DOES_NOT_EXIST = \"Table doesn't exist with the name of \";\n\n    /**\n     * Don't have permission to create database on sdcard.\n     */\n    public static final String EXTERNAL_STORAGE_PERMISSION_DENIED = \"You don't have permission to access database at %1$s. Make sure you handled WRITE_EXTERNAL_STORAGE runtime permission correctly.\";\n\n\t/**\n\t * Constructor of DatabaseGenerateException.\n\t * \n\t * @param errorMessage\n\t *            the description of this DatabaseGenerateException.\n\t */\n\tpublic DatabaseGenerateException(String errorMessage) {\n\t\tsuper(errorMessage);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/exceptions/GlobalException.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.exceptions;\n\n/**\n * This is where all the global exceptions declared of LitePal.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class GlobalException extends RuntimeException {\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Application context is null.\n\t */\n\tpublic static final String APPLICATION_CONTEXT_IS_NULL = \"Application context is null. Maybe you neither configured your application name with \\\"org.litepal.LitePalApplication\\\" in your AndroidManifest.xml, nor called LitePal.initialize(Context) method.\";\n\n\t/**\n\t * Constructor of GlobalException.\n\t * \n\t * @param errorMessage\n\t *            the description of this exception.\n\t */\n\tpublic GlobalException(String errorMessage) {\n\t\tsuper(errorMessage);\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/exceptions/InvalidAttributesException.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.exceptions;\n\n/**\n * Reading the attributes in the litepal.xml file. Check all the attributes if they\n * are valid value. If anyone of them is not under rules, throw\n * InvalidAttributesException exception.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class InvalidAttributesException extends RuntimeException {\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * dbname is empty or not defined in litepal.xml file.\n\t */\n\tpublic static final String DBNAME_IS_EMPTY_OR_NOT_DEFINED = \"dbname is empty or not defined in litepal.xml file, or your litepal.xml file is missing.\";\n\n\t/**\n\t * the version of database can not be less than 1.\n\t */\n\tpublic static final String VERSION_OF_DATABASE_LESS_THAN_ONE = \"the version of database can not be less than 1\";\n\n\t/**\n\t * the version in litepal.xml is earlier than the current version.\n\t */\n\tpublic static final String VERSION_IS_EARLIER_THAN_CURRENT = \"the version in litepal.xml is earlier than the current version\";\n\t\n\t/**\n\t * There's an invalid value in cases mark. Only keep, upper, lower allowed.\n\t */\n\tpublic static final String CASES_VALUE_IS_INVALID = \" is an invalid value for <cases></cases>\";\n\n\t/**\n\t * Constructor of InvalidAttributesException.\n\t * \n\t * @param errorMessage\n\t *            the description of this InvalidAttributesException.\n\t */\n\tpublic InvalidAttributesException(String errorMessage) {\n\t\tsuper(errorMessage);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/exceptions/LitePalSupportException.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.exceptions;\n\n/**\n * When LitePal deals with CRUD actions of LitePalSupport, it may throw\n * LitePalSupportException.\n * \n * @author Tony Green\n * @since 2.0\n */\npublic class LitePalSupportException extends DataSupportException {\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Thrown when models have invalid type for id fields. Only int or long is\n\t * supported.\n\t */\n\tpublic static final String ID_TYPE_INVALID_EXCEPTION = \"id type is not supported. Only int or long is acceptable for id\";\n\n\t/**\n\t * Thrown when the saving model is not an instance of LitePalSupport.\n\t */\n\tpublic static final String MODEL_IS_NOT_AN_INSTANCE_OF_LITE_PAL_SUPPORT = \" should be inherited from LitePalSupport\";\n\n\t/**\n\t * Thrown when developers use wrong field to declare many2one or many2many\n\t * associations.\n\t */\n\tpublic static final String WRONG_FIELD_TYPE_FOR_ASSOCIATIONS = \"The field to declare many2one or many2many associations should be List or Set.\";\n\n\t/**\n\t * Thrown when fail to save a model.\n\t */\n\tpublic static final String SAVE_FAILED = \"Save current model failed.\";\n\n\t/**\n\t * Thrown when there is no default constructor in model class to update.\n\t */\n\tpublic static final String INSTANTIATION_EXCEPTION = \" needs a default constructor.\";\n\n\t/**\n\t * Thrown when the parameters in conditions are incorrect.\n\t */\n\tpublic static final String UPDATE_CONDITIONS_EXCEPTION = \"The parameters in conditions are incorrect.\";\n\n\t/**\n\t * Constructor of LitePalSupportException.\n\t * \n\t * @param errorMessage\n\t *            the description of this exception.\n\t */\n\tpublic LitePalSupportException(String errorMessage) {\n\t\tsuper(errorMessage);\n\t}\n\n    /**\n     * Constructor of LitePalSupportException.\n     *\n     * @param errorMessage\n     *            the description of this exception.\n     * @param throwable\n     *            the cause of this exception.\n     */\n    public LitePalSupportException(String errorMessage, Throwable throwable) {\n        super(errorMessage, throwable);\n    }\n\n\t/**\n\t * Thrown when the VM notices that a program tries to reference, on a class\n\t * or object, a method that does not exist.\n\t * \n\t * @param className\n\t *            The class name.\n\t * @param methodName\n\t *            The method name which is missing.\n\t * @return Exception message.\n\t */\n\tpublic static String noSuchMethodException(String className, String methodName) {\n\t\treturn \"The \" + methodName + \" method in \" + className\n\t\t\t\t+ \" class is necessary which does not exist.\";\n\t}\n\n\t/**\n\t * Thrown when the virtual machine notices that a program tries to\n\t * reference, on a class or object, a field that does not exist.\n\t * \n\t * @param className\n\t *            The class name.\n\t * @param fieldName\n\t *            The field name which is missing.\n\t * @return Exception message.\n\t */\n\tpublic static String noSuchFieldExceptioin(String className, String fieldName) {\n\t\treturn \"The \" + fieldName + \" field in \" + className\n\t\t\t\t+ \" class is necessary which does not exist.\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/exceptions/ParseConfigurationFileException.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.exceptions;\n\n/**\n * Using SAX way parsing XML by default. If any problem happens when parsing the\n * litepal.xml file, ParseConfigurationFileException will be thrown.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class ParseConfigurationFileException extends RuntimeException {\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * can not find the litepal.xml file by the given id.\n\t */\n\tpublic static final String CAN_NOT_FIND_LITEPAL_FILE = \"litepal.xml file is missing. Please ensure it under assets folder.\";\n\n\t/**\n\t * can not parse the litepal.xml, check if it's in correct format.\n\t */\n\tpublic static final String FILE_FORMAT_IS_NOT_CORRECT = \"can not parse the litepal.xml, check if it's in correct format\";\n\n\t/**\n\t * parse configuration is failed.\n\t */\n\tpublic static final String PARSE_CONFIG_FAILED = \"parse configuration is failed\";\n\n\t/**\n\t * IO exception happened.\n\t */\n\tpublic static final String IO_EXCEPTION = \"IO exception happened\";\n\n\t/**\n\t * Constructor of ParseConfigurationFileException.\n\t * \n\t * @param errorMessage\n\t *            the description of this exception.\n\t */\n\tpublic ParseConfigurationFileException(String errorMessage) {\n\t\tsuper(errorMessage);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/extension/FluentQuery.kt",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.extension\n\nimport org.litepal.FluentQuery\nimport org.litepal.crud.async.FindExecutor\n\n/**\n * Extension of FluentQuery class for Kotlin api.\n * @author Tony Green\n * @since 2.1\n */\n\n/**\n * Finds multiple records by the cluster parameters. You can use the below\n * way to finish a complicated query:\n * ```\n * LitePal.select(\"name\").where(\"age > ?\", \"14\").order(\"age\").limit(1).offset(2).find<Person>()\n * ```\n * You can also do the same job with SQLiteDatabase like this:\n * ```\n * getSQLiteDatabase().query(\"Person\", \"name\", \"age > ?\", new String[] { \"14\" }, null, null, \"age\",\n * \t\t\"2,1\")\n * ```\n * Obviously, the first way is much more semantic.<br>\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * {@link FluentQuery#find(Class, boolean)}.\n *\n * @return An object list with founded data from database, or an empty list.\n */\ninline fun <reified T> FluentQuery.find(): List<T> = find(T::class.java)\n\n/**\n * Basically same as {@link #find(Class)} but pending to a new thread for executing.\n *\n * @return A FindMultiExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> FluentQuery.findAsync() = findAsync(T::class.java)\n\n/**\n * It is mostly same as [FluentQuery.find(Class)] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n *            True to load the associated models, false not.\n * @return An object list with founded data from database, or an empty list.\n */\ninline fun <reified T> FluentQuery.find(isEager: Boolean): List<T> = find(T::class.java, isEager)\n\n/**\n * Basically same as {@link #find(Class, boolean)} but pending to a new thread for executing.\n *\n * @param isEager\n *            True to load the associated models, false not.\n * @return A FindMultiExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> FluentQuery.findAsync(isEager: Boolean) = findAsync(T::class.java, isEager)\n\n/**\n * Finds the first record by the cluster parameters. You can use the below\n * way to finish a complicated query:\n * ```\n * LitePal.select(\"name\").where(\"age > ?\", \"14\").order(\"age\").limit(10).offset(2).findFirst<Person>()\n * ```\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * [FluentQuery.findFirst(Class, boolean)].\n *\n * @return An object with founded data from database, or null.\n */\ninline fun <reified T> FluentQuery.findFirst(): T? = findFirst(T::class.java)\n\n/**\n * Basically same as {@link #findFirst(Class)} but pending to a new thread for executing.\n *\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> FluentQuery.findFirstAsync(): FindExecutor<T> = findFirstAsync(T::class.java)\n\n/**\n * It is mostly same as [FluentQuery.findFirst(Class)] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n *            True to load the associated models, false not.\n * @return An object with founded data from database, or null.\n */\ninline fun <reified T> FluentQuery.findFirst(isEager: Boolean): T? = findFirst(T::class.java, isEager)\n\n/**\n * Finds the last record by the cluster parameters. You can use the below\n * way to finish a complicated query:\n * ```\n * LitePal.select(\"name\").where(\"age > ?\", \"14\").order(\"age\").limit(10).offset(2).findLast<Person>()\n * ```\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * [FluentQuery.findLast(Class, boolean)].\n *\n * @return An object with founded data from database, or null.\n */\ninline fun <reified T> FluentQuery.findLast(): T? = findLast(T::class.java)\n\n/**\n * It is mostly same as [FluentQuery.findLast(Class)] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n *            True to load the associated models, false not.\n * @return An object with founded data from database, or null.\n */\ninline fun <reified T> FluentQuery.findLast(isEager: Boolean): T? = findLast(T::class.java, isEager)\n\n/**\n * Count the records.\n * ```\n * LitePal.count<Person>()\n * ```\n * This will count all rows in person table.\n *\n * You can also specify a where clause when counting.\n * ```\n * LitePal.where(\"age > ?\", \"15\").count<Person>()\n * ```\n * @return Count of the specified table.\n */\ninline fun <reified T> FluentQuery.count() = count(T::class.java)\n\n/**\n * Calculates the average value on a given column.\n * ```\n * LitePal.average<Person>(\"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").average<Person>(\"age\")\n * ```\n * @param column\n * The based on column to calculate.\n * @return The average value on a given column.\n */\ninline fun <reified T> FluentQuery.average(column: String) = average(T::class.java, column)\n\n/**\n * Calculates the maximum value on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.max<Person, Int>(\"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").max<Person, Int>(\"age\")\n * ```\n * @param columnName\n * The based on column to calculate.\n *\n * @return The maximum value on a given column.\n */\ninline fun <reified T, reified R> FluentQuery.max(columnName: String): R = max(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the maximum value on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.max<Int>(\"person\", \"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").max<Int>(\"person\", \"age\")\n * ```\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The maximum value on a given column.\n */\ninline fun <reified R> FluentQuery.max(tableName: String, columnName: String): R = max(tableName, columnName, R::class.java)\n\n/**\n * Calculates the minimum value on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.min<Person, Int>(\"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").min<Person, Int>(\"age\")\n * ```\n * @param columnName\n * The based on column to calculate.\n * @return The minimum value on a given column.\n */\ninline fun <reified T, reified R> FluentQuery.min(columnName: String): R = min(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the minimum value on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.min<Int>(\"person\", \"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").min<Int>(\"person\", \"age\")\n * ```\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The minimum value on a given column.\n */\ninline fun <reified R> FluentQuery.min(tableName: String, columnName: String): R = min(tableName, columnName, R::class.java)\n\n/**\n * Calculates the sum of values on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.sum<Person, Int>(\"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").sum<Person, Int>(\"age\")\n * ```\n * @param columnName\n * The based on column to calculate.\n * @return The sum value on a given column.\n */\ninline fun <reified T, reified R> FluentQuery.sum(columnName: String): R = sum(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the sum of values on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.sum<Int>(\"person\", \"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").sum<Int>(\"person\", \"age\")\n * ```\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The sum value on a given column.\n */\ninline fun <reified R> FluentQuery.sum(tableName: String, columnName: String): R = sum(tableName, columnName, R::class.java)"
  },
  {
    "path": "core/src/main/java/org/litepal/extension/LitePal.kt",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.extension\n\nimport android.content.ContentValues\nimport org.litepal.LitePal\nimport org.litepal.crud.LitePalSupport\nimport java.lang.Exception\n\n/**\n * Extension of LitePal class for Kotlin api.\n * @author Tony Green\n * @since 2.1\n */\n\n/**\n * Count the records.\n * ```\n * LitePal.count<Person>()\n * ```\n * This will count all rows in person table.\n *\n * You can also specify a where clause when counting.\n * ```\n * LitePal.where(\"age > ?\", \"15\").count<Person>()\n * ```\n * @return Count of the specified table.\n */\ninline fun <reified T> LitePal.count() = count(T::class.java)\n\n/**\n * Basically same as [LitePal.count] but pending to a new thread for executing.\n *\n * @return A CountExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.countAsync() = countAsync(T::class.java)\n\n/**\n * Calculates the average value on a given column.\n * ```\n * LitePal.average<Person>(\"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").average<Person>(\"age\")\n * ```\n * @param column\n * The based on column to calculate.\n * @return The average value on a given column.\n */\ninline fun <reified T> LitePal.average(column: String) = average(T::class.java, column)\n\n/**\n * Basically same as [LitePal.average] but pending to a new thread for executing.\n *\n * @param column\n * The based on column to calculate.\n * @return A AverageExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.averageAsync(column: String) = averageAsync(T::class.java, column)\n\n/**\n * Calculates the maximum value on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.max<Person, Int>(\"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").max<Person, Int>(\"age\")\n * ```\n * @param columnName\n * The based on column to calculate.\n *\n * @return The maximum value on a given column.\n */\ninline fun <reified T, reified R> LitePal.max(columnName: String) = max(T::class.java, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.max] but pending to a new thread for executing.\n *\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T, reified R> LitePal.maxAsync(columnName: String) = maxAsync(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the maximum value on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.max<Int>(\"person\", \"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").max<Int>(\"person\", \"age\")\n * ```\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The maximum value on a given column.\n */\ninline fun <reified R> LitePal.max(tableName: String, columnName: String) = max(tableName, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.max] but pending to a new thread for executing.\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified R> LitePal.maxAsync(tableName: String, columnName: String) = maxAsync(tableName, columnName, R::class.java)\n\n/**\n * Calculates the minimum value on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.min<Person, Int>(\"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").min<Person, Int>(\"age\")\n * ```\n * @param columnName\n * The based on column to calculate.\n * @return The minimum value on a given column.\n */\ninline fun <reified T, reified R> LitePal.min(columnName: String) = min(T::class.java, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.min] but pending to a new thread for executing.\n *\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T, reified R> LitePal.minAsync(columnName: String) = minAsync(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the minimum value on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.min<Int>(\"person\", \"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").min<Int>(\"person\", \"age\")\n * ```\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The minimum value on a given column.\n */\ninline fun <reified R> LitePal.min(tableName: String, columnName: String) = min(tableName, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.min] but pending to a new thread for executing.\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified R> LitePal.minAsync(tableName: String, columnName: String) = minAsync(tableName, columnName, R::class.java)\n\n/**\n * Calculates the sum of values on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.sum<Person, Int>(\"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").sum<Person, Int>(\"age\")\n * ```\n * @param columnName\n * The based on column to calculate.\n * @return The sum value on a given column.\n */\ninline fun <reified T, reified R> LitePal.sum(columnName: String) = sum(T::class.java, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.sum] but pending to a new thread for executing.\n *\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T, reified R> LitePal.sumAsync(columnName: String) = sumAsync(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the sum of values on a given column. The value is returned\n * with the same data type of the column.\n * ```\n * LitePal.sum<Int>(\"person\", \"age\")\n * ```\n * You can also specify a where clause when calculating.\n * ```\n * LitePal.where(\"age > ?\", \"15\").sum<Int>(\"person\", \"age\")\n * ```\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The sum value on a given column.\n */\ninline fun <reified R> LitePal.sum(tableName: String, columnName: String) = sum(tableName, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.sum] but pending to a new thread for executing.\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified R> LitePal.sumAsync(tableName: String, columnName: String) = sumAsync(tableName, columnName, R::class.java)\n\n/**\n * Finds the record by a specific id.\n * ```\n * val person = LitePal.find<Person>(1)\n * ```\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using[LitePal.find] with isEager parameter.\n *\n * @param id\n * Which record to query.\n * @return An object with found data from database, or null.\n */\ninline fun <reified T> LitePal.find(id: Long): T? = find(T::class.java, id)\n\n/**\n * Basically same as [LitePal.find] but pending to a new thread for executing.\n *\n * @param id\n * Which record to query.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.findAsync(id: Long) = findAsync(T::class.java, id)\n\n/**\n * It is mostly same as [LitePal.find] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param id\n * Which record to query.\n * @param isEager\n * True to load the associated models, false not.\n * @return An object with found data from database, or null.\n */\ninline fun <reified T> LitePal.find(id: Long, isEager: Boolean) = find(T::class.java, id, isEager)\n\n/**\n * Basically same as [LitePal.find] but pending to a new thread for executing.\n *\n * @param id\n * Which record to query.\n * @param isEager\n * True to load the associated models, false not.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.findAsync(id: Long, isEager: Boolean) = find(T::class.java, id, isEager)\n\n/**\n * Finds the first record of a single table.\n * ```\n * val person = LitePal.findFirst<Person>()\n * ```\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * [LitePal.findFirst] with isEager parameter.\n *\n * @return An object with data of first row, or null.\n */\ninline fun <reified T> LitePal.findFirst() = findFirst(T::class.java)\n\n/**\n * Basically same as [LitePal.findFirst] but pending to a new thread for executing.\n *\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.findFirstAsync() = findFirstAsync(T::class.java)\n\n/**\n * It is mostly same as [LitePal.findFirst] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @return An object with data of first row, or null.\n */\ninline fun <reified T> LitePal.findFirst(isEager: Boolean) = findFirst(T::class.java, isEager)\n\n/**\n * Basically same as [LitePal.findFirst] but pending to a new thread for executing.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.findFirstAsync(isEager: Boolean) = findFirstAsync(T::class.java, isEager)\n\n/**\n * Finds the last record of a single table.\n * ```\n * val p = LitePal.findLast<Person>()\n * ```\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * [LitePal.findLast] with isEager parameter.\n *\n * @return An object with data of last row, or null.\n */\ninline fun <reified T> LitePal.findLast() = findLast(T::class.java)\n\n/**\n * Basically same as [LitePal.findLast] but pending to a new thread for executing.\n *\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.findLastAsync() = findLastAsync(T::class.java)\n\n/**\n * It is mostly same as [LitePal.findLast] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @return An object with data of last row, or null.\n */\ninline fun <reified T> LitePal.findLast(isEager: Boolean) = findLast(T::class.java, isEager)\n\n/**\n * Basically same as [LitePal.findLast] but pending to a new thread for executing.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @return A FindExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.findLastAsync(isEager: Boolean) = findLastAsync(T::class.java, isEager)\n\n/**\n * Finds multiple records by an id array.\n * ```\n * val people = LitePal.findAll<Person>(1, 2, 3)\n * val bookIds = longArrayOf(10, 18)\n * LitePal.findAll<Book>(*bookIds)\n * ```\n * Of course you can find all records by passing nothing to the ids\n * parameter.\n *\n * val allBooks = LitePal.findAll<Book>()\n *\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * [LitePal.findAll] with isEager parameter.\n *\n * @param ids\n * Which records to query. Or do not pass it to find all records.\n * @return An object list with found data from database, or an empty list.\n */\ninline fun <reified T> LitePal.findAll(vararg ids: Long) = findAll(T::class.java, *ids)\n\n/**\n * Basically same as [LitePal.findAll] but pending to a new thread for executing.\n *\n * @param ids\n * Which records to query. Or do not pass it to find all records.\n * @return A FindMultiExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.findAllAsync(vararg ids: Long) = findAllAsync(T::class.java, *ids)\n\n/**\n * It is mostly same as [LitePal.findAll] but an\n * isEager parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @param ids\n * Which records to query. Or do not pass it to find all records.\n * @return An object list with found data from database, or an empty list.\n */\ninline fun <reified T> LitePal.findAll(isEager: Boolean, vararg ids: Long) = findAll(T::class.java, isEager, *ids)\n\n/**\n * Basically same as [LitePal.findAll] but pending to a new thread for executing.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @param ids\n * Which records to query. Or do not pass it to find all records.\n * @return A FindMultiExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.findAllAsync(isEager: Boolean, vararg ids: Long) = findAllAsync(T::class.java, isEager, *ids)\n\n/**\n * Deletes the record in the database by id.\n *\n * The data in other tables which is referenced with the record will be\n * removed too.\n * ```\n * LitePal.delete<Person>(1)\n * ```\n * This means that the record 1 in person table will be removed.\n *\n * @param id\n * Which record to delete.\n * @return The number of rows affected. Including cascade delete rows.\n */\ninline fun <reified T> LitePal.delete(id: Long) = delete(T::class.java, id)\n\n/**\n * Basically same as [LitePal.delete] but pending to a new thread for executing.\n *\n * @param id\n * Which record to delete.\n * @return A UpdateOrDeleteExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.deleteAsync(id: Long) = deleteAsync(T::class.java, id)\n\n/**\n * Deletes all records with details given if they match a set of conditions\n * supplied. This method constructs a single SQL DELETE statement and sends\n * it to the database.\n * ```\n * LitePal.deleteAll<Person>(\"name = ? and age = ?\", \"Tom\", \"14\")\n * ```\n * This means that all the records which name is Tom and age is 14 will be\n * removed.\n *\n * @param conditions\n * A string array representing the WHERE part of an SQL\n * statement. First parameter is the WHERE clause to apply when\n * deleting. The way of specifying place holders is to insert one\n * or more question marks in the SQL. The first question mark is\n * replaced by the second element of the array, the next question\n * mark by the third, and so on. Passing empty string will delete\n * all rows.\n * @return The number of rows affected.\n */\ninline fun <reified T> LitePal.deleteAll(vararg conditions: String?) = deleteAll(T::class.java, *conditions)\n\n/**\n * Basically same as [LitePal.deleteAll] but pending to a new thread for executing.\n *\n * @param conditions\n * A string array representing the WHERE part of an SQL\n * statement. First parameter is the WHERE clause to apply when\n * deleting. The way of specifying place holders is to insert one\n * or more question marks in the SQL. The first question mark is\n * replaced by the second element of the array, the next question\n * mark by the third, and so on. Passing empty string will delete\n * all rows.\n * @return A UpdateOrDeleteExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.deleteAllAsync(vararg conditions: String?) = deleteAllAsync(T::class.java, *conditions)\n\n/**\n * Updates the corresponding record by id with ContentValues. Returns the\n * number of affected rows.\n * ```\n * val cv = ContentValues()\n * cv.put(\"name\", \"Jim\")\n * LitePal.update<Person>(cv, 1)\n * ```\n * This means that the name of record 1 will be updated into Jim.\n *\n * @param values\n * A map from column names to new column values. null is a valid\n * value that will be translated to NULL.\n * @param id\n * Which record to update.\n * @return The number of rows affected.\n */\ninline fun <reified T> LitePal.update(values: ContentValues, id: Long) = update(T::class.java, values, id)\n\n/**\n * Basically same as [LitePal.update] but pending to a new thread for executing.\n *\n * @param values\n * A map from column names to new column values. null is a valid\n * value that will be translated to NULL.\n * @param id\n * Which record to update.\n * @return A UpdateOrDeleteExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.updateAsync(values: ContentValues, id: Long) = updateAsync(T::class.java, values, id)\n\n/**\n * Updates all records with details given if they match a set of conditions\n * supplied. This method constructs a single SQL UPDATE statement and sends\n * it to the database.\n * ```\n * val cv = ContentValues()\n * cv.put(\"name\", \"Jim\")\n * LitePal.update<Person>(cv, \"name = ?\", \"Tom\")\n * ```\n * This means that all the records which name is Tom will be updated into\n * Jim.\n *\n * @param values\n * A map from column names to new column values. null is a valid\n * value that will be translated to NULL.\n * @param conditions\n * A string array representing the WHERE part of an SQL\n * statement. First parameter is the WHERE clause to apply when\n * updating. The way of specifying place holders is to insert one\n * or more question marks in the SQL. The first question mark is\n * replaced by the second element of the array, the next question\n * mark by the third, and so on. Passing empty string will update\n * all rows.\n * @return The number of rows affected.\n */\ninline fun <reified T> LitePal.updateAll(values: ContentValues, vararg conditions: String?) = updateAll(T::class.java, values, *conditions)\n\n/**\n * Basically same as [LitePal.updateAll] but pending to a new thread for executing.\n *\n * @param values\n * A map from column names to new column values. null is a valid\n * value that will be translated to NULL.\n * @param conditions\n * A string array representing the WHERE part of an SQL\n * statement. First parameter is the WHERE clause to apply when\n * updating. The way of specifying place holders is to insert one\n * or more question marks in the SQL. The first question mark is\n * replaced by the second element of the array, the next question\n * mark by the third, and so on. Passing empty string will update\n * all rows.\n * @return A UpdateOrDeleteExecutor instance.\n */\n@Deprecated(\"This method is deprecated and will be removed in the future releases.\", ReplaceWith(\"Handle async db operation in your own logic instead.\"))\ninline fun <reified T> LitePal.updateAllAsync(values: ContentValues, vararg conditions: String?) = updateAllAsync(T::class.java, values, *conditions)\n\n/**\n * Check if the specified conditions data already exists in the table.\n * @param conditions\n * A filter declaring which data to check. Exactly same use as\n * [LitePal.where], except null conditions will result in false.\n * @return Return true if the specified conditions data already exists in the table.\n * False otherwise. Null conditions will result in false.\n */\ninline fun <reified T> LitePal.isExist(vararg conditions: String?) = isExist(T::class.java, *conditions)\n\n/**\n * Saves the collection into database.\n * ```\n * val people = listOf<Person>(...)\n * people.saveAll();\n * ```\n * If the model in collection is a new record gets created in the database,\n * otherwise the existing record gets updated.\n *\n * If saving process failed by any accident, the whole action will be\n * cancelled and your database will be **rolled back**.\n *\n * This method acts the same result as the below way, but **much more\n * efficient**.\n * ```\n * for (person in people) {\n *      person.save()\n * }\n * ```\n *\n * @param collection\n * Holds all models to save.\n * @return True if all records in collection are saved. False none record in collection is saved. There won't be partial saved condition.\n */\nfun <T : LitePalSupport> Collection<T>.saveAll() = LitePal.saveAll(this)\n\n/**\n * Open a transaction scope, all codes in the lambda will under transaction.\n * If lambda return true, all db operations in lambda will be committed.\n * Otherwise all db operations will be rolled back.\n */\n@Synchronized fun LitePal.runInTransaction(block: () -> Boolean): Boolean {\n    beginTransaction()\n    val succeeded = try {\n        block()\n    } catch (e: Exception) {\n        false\n    }\n    if (succeeded) {\n        setTransactionSuccessful()\n    }\n    endTransaction()\n    return succeeded\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/model/Table_Schema.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.model;\n\n/**\n * This class is a constant model class. It stores each table name of the\n * corresponding model classes added by developers. When synchronizing the\n * tables with the model classes, the table names are necessary to decide which\n * tables are created by the developers and which are created by system. The\n * values in table_schema are totally generated automatically, do not try to\n * change any value in it or the synchronization might be failed.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class Table_Schema {\n\n\t/**\n\t * The table name of model class.\n\t */\n\tprivate String name;\n\n\t/**\n\t * Type of the table. 0 normal table, 1 intermediate join table.\n\t */\n\tprivate int type;\n\n\t/**\n\t * Get the table name.\n\t * \n\t * @return The table name.\n\t */\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t/**\n\t * Set the table name.\n\t * \n\t * @param name\n\t *            The table name.\n\t */\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t/**\n\t * Get the table type.\n\t * \n\t * @return The table type.\n\t */\n\tpublic int getType() {\n\t\treturn type;\n\t}\n\n\t/**\n\t * Set the table type.\n\t * \n\t * @param type\n\t *            The table type to set\n\t */\n\tpublic void setType(int type) {\n\t\tthis.type = type;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/parser/LitePalAttr.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.parser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.litepal.exceptions.InvalidAttributesException;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.Const;\nimport org.litepal.util.SharedUtil;\n\nimport android.text.TextUtils;\n\n/**\n * The object model for the litepal.xml file. Once database connection happens,\n * LitePal will try to analysis the litepal.xml, and read all the attribute into\n * the LitePalAttr model for further usage.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic final class LitePalAttr {\n\n\t/**\n\t * Static litePalAttr object.\n\t */\n\tprivate static LitePalAttr litePalAttr;\n\n\t/**\n\t * The version of database.\n\t */\n\tprivate int version;\n\n\t/**\n\t * The name of database.\n\t */\n\tprivate String dbName;\n\n\t/**\n\t * The case of table names and column names and SQL.\n\t */\n\tprivate String cases;\n\n    /**\n     * Define where the .db file should be. Option values: internal, external, or path in sdcard.\n     */\n    private String storage;\n\n\t/**\n\t * All the model classes that want to map in the database. Each class should\n\t * be given the full name including package name.\n\t */\n\tprivate List<String> classNames;\n\n    /**\n     * Extra name as key for saving the database version in SharedUtil.\n     */\n\tprivate String extraKeyName;\n\n\t/**\n\t * Do not allow new a LitePalAttr object. Makes it a singleton class.\n\t */\n\tprivate LitePalAttr() {\n\t}\n\n\t/**\n\t * Provide a way to get the instance of LitePalAttr.\n\t * @return the singleton instance of LitePalAttr\n\t */\n\tpublic static LitePalAttr getInstance() {\n\t\tif (litePalAttr == null) {\n\t\t\tsynchronized (LitePalAttr.class) {\n\t\t\t\tif (litePalAttr == null) {\n\t\t\t\t\tlitePalAttr = new LitePalAttr();\n                    loadLitePalXMLConfiguration();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn litePalAttr;\n\t}\n\n\tprivate static void loadLitePalXMLConfiguration() {\n        if (BaseUtility.isLitePalXMLExists()) {\n            LitePalConfig config = LitePalParser.parseLitePalConfiguration();\n            litePalAttr.setDbName(config.getDbName());\n            litePalAttr.setVersion(config.getVersion());\n            litePalAttr.setClassNames(config.getClassNames());\n            litePalAttr.setCases(config.getCases());\n            litePalAttr.setStorage(config.getStorage());\n        }\n    }\n\n\t/**\n\t * Clear the instance of LitePalAttr.\n\t */\n\tpublic static void clearInstance() {\n\t\tlitePalAttr = null;\n\t}\n\n\tpublic int getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic void setVersion(int version) {\n\t\tthis.version = version;\n\t}\n\n\tpublic String getDbName() {\n\t\treturn dbName;\n\t}\n\n\tpublic void setDbName(String dbName) {\n\t\tthis.dbName = dbName;\n\t}\n\n    public String getStorage() {\n        return storage;\n    }\n\n\tpublic void setStorage(String storage) {\n        this.storage = storage;\n    }\n\n    public String getExtraKeyName() {\n        return extraKeyName;\n    }\n\n    public void setExtraKeyName(String extraKeyName) {\n        this.extraKeyName = extraKeyName;\n    }\n\n    /**\n\t * Get the class name list. Always add table_schema as a value.\n\t * \n\t * @return The class name list.\n\t */\n\tpublic List<String> getClassNames() {\n\t\tif (classNames == null) {\n\t\t\tclassNames = new ArrayList<String>();\n\t\t\tclassNames.add(\"org.litepal.model.Table_Schema\");\n\t\t} else if (classNames.isEmpty()) {\n\t\t\tclassNames.add(\"org.litepal.model.Table_Schema\");\n\t\t}\n\t\treturn classNames;\n\t}\n\n\t/**\n\t * Add a class name into the current mapping model list.\n\t * \n\t * @param className\n\t *            Full package class name.\n\t */\n\tpublic void addClassName(String className) {\n\t\tgetClassNames().add(className);\n\t}\n\n\tpublic void setClassNames(List<String> classNames) {\n\t\tthis.classNames = classNames;\n\t}\n\n\tpublic String getCases() {\n\t\treturn cases;\n\t}\n\n\tpublic void setCases(String cases) {\n\t\tthis.cases = cases;\n\t}\n\n\t/**\n\t * Before application build the connection with database, check the fields\n\t * in LitePalAttr. If all of the fields are passed, the connection will be\n\t * continued.If anyone of them doesn't pass, an exception will be thrown.\n\t * If dbname is undefined, or version is less than 1, or version is earlier\n\t * than current version, throw InvalidAttributesException.\n\t * \n\t * @throws org.litepal.exceptions.InvalidAttributesException\n\t */\n\tpublic void checkSelfValid() {\n\t\tif (TextUtils.isEmpty(dbName)) {\n            loadLitePalXMLConfiguration();\n            if (TextUtils.isEmpty(dbName)) {\n                throw new InvalidAttributesException(\n                        InvalidAttributesException.DBNAME_IS_EMPTY_OR_NOT_DEFINED);\n            }\n\t\t}\n\t\tif (!dbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {\n\t\t\tdbName = dbName + Const.Config.DB_NAME_SUFFIX;\n\t\t}\n\t\tif (version < 1) {\n\t\t\tthrow new InvalidAttributesException(\n\t\t\t\t\tInvalidAttributesException.VERSION_OF_DATABASE_LESS_THAN_ONE);\n\t\t}\n\t\tif (version < SharedUtil.getLastVersion(extraKeyName)) {\n\t\t\tthrow new InvalidAttributesException(\n\t\t\t\t\tInvalidAttributesException.VERSION_IS_EARLIER_THAN_CURRENT);\n\t\t}\n\t\tif (TextUtils.isEmpty(cases)) {\n\t\t\tcases = Const.Config.CASES_LOWER;\n\t\t} else {\n\t\t\tif (!cases.equals(Const.Config.CASES_UPPER)\n\t\t\t\t\t&& !cases.equals(Const.Config.CASES_LOWER)\n\t\t\t\t\t&& !cases.equals(Const.Config.CASES_KEEP)) {\n\t\t\t\tthrow new InvalidAttributesException(cases\n\t\t\t\t\t\t+ InvalidAttributesException.CASES_VALUE_IS_INVALID);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/parser/LitePalConfig.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.parser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Model for litepal.xml configuration file.\n * @author guolin\n * @since 2016/11/10\n */\npublic class LitePalConfig {\n\n    /**\n     * The version of database.\n     */\n    private int version;\n\n    /**\n     * The name of database.\n     */\n    private String dbName;\n\n    /**\n     * The case of table names and column names and SQL.\n     */\n    private String cases;\n\n    /**\n     * Define where the .db file should be. Option values: internal external.\n     */\n    private String storage;\n\n    /**\n     * All the model classes that want to map in the database. Each class should\n     * be given the full name including package name.\n     */\n    private List<String> classNames;\n\n    public int getVersion() {\n        return version;\n    }\n\n    public void setVersion(int version) {\n        this.version = version;\n    }\n\n    public String getDbName() {\n        return dbName;\n    }\n\n    public void setDbName(String dbName) {\n        this.dbName = dbName;\n    }\n\n    public String getStorage() {\n        return storage;\n    }\n\n    public void setStorage(String storage) {\n        this.storage = storage;\n    }\n\n    /**\n     * Get the class name list. Always add table_schema as a value.\n     *\n     * @return The class name list.\n     */\n    public List<String> getClassNames() {\n        if (classNames == null) {\n            classNames = new ArrayList<String>();\n            classNames.add(\"org.litepal.model.Table_Schema\");\n        } else if (classNames.isEmpty()) {\n            classNames.add(\"org.litepal.model.Table_Schema\");\n        }\n        return classNames;\n    }\n\n    /**\n     * Add a class name into the current mapping model list.\n     *\n     * @param className\n     *            Full package class name.\n     */\n    public void addClassName(String className) {\n        getClassNames().add(className);\n    }\n\n    public void setClassNames(List<String> classNames) {\n        this.classNames = classNames;\n    }\n\n    public String getCases() {\n        return cases;\n    }\n\n    public void setCases(String cases) {\n        this.cases = cases;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/parser/LitePalContentHandler.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.parser;\n\nimport org.xml.sax.Attributes;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.helpers.DefaultHandler;\n\n/**\n * This is the content handler for analysis the litepal.xml file by SAXParser,\n * and temporarily the only correct way to generate LitePalAttr model with\n * values.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class LitePalContentHandler extends DefaultHandler {\n\n\t/**\n\t * Store the parsed value of litepal.xml.\n\t */\n\tprivate LitePalAttr litePalAttr;\n\n\t/**\n\t * Characters in the characters tag. Decide to not use this method\n\t * temporarily. Use value attribute instead.\n\t */\n\t@Override\n\tpublic void characters(char[] ch, int start, int length) throws SAXException {\n\t}\n\n\t/**\n\t * End of the document. Doing nothing temporarily.\n\t */\n\t@Override\n\tpublic void endDocument() throws SAXException {\n\t}\n\n\t/**\n\t * End of the element. Doing nothing temporarily.\n\t */\n\t@Override\n\tpublic void endElement(String uri, String localName, String qName) throws SAXException {\n\t}\n\n\t/**\n\t * Start of the document. Generate a LitePalAttr model at the same time.\n\t */\n\t@Override\n\tpublic void startDocument() throws SAXException {\n\t\tlitePalAttr = LitePalAttr.getInstance();\n\t\tlitePalAttr.getClassNames().clear();\n\t}\n\n\t/**\n\t * Start analysis the litepal.xml file. Set all the parsed value into the\n\t * LitePalAttr model.\n\t */\n\t@Override\n\tpublic void startElement(String uri, String localName, String qName, Attributes attributes)\n\t\t\tthrows SAXException {\n\t\tif (LitePalParser.NODE_DB_NAME.equalsIgnoreCase(localName)) {\n\t\t\tfor (int i = 0; i < attributes.getLength(); i++) {\n\t\t\t\tif (LitePalParser.ATTR_VALUE.equalsIgnoreCase(attributes.getLocalName(i))) {\n\t\t\t\t\tlitePalAttr.setDbName(attributes.getValue(i).trim());\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (LitePalParser.NODE_VERSION.equalsIgnoreCase(localName)) {\n\t\t\tfor (int i = 0; i < attributes.getLength(); i++) {\n\t\t\t\tif (LitePalParser.ATTR_VALUE.equalsIgnoreCase(attributes.getLocalName(i))) {\n\t\t\t\t\tlitePalAttr.setVersion(Integer.parseInt(attributes.getValue(i).trim()));\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (LitePalParser.NODE_MAPPING.equalsIgnoreCase(localName)) {\n\t\t\tfor (int i = 0; i < attributes.getLength(); i++) {\n\t\t\t\tif (LitePalParser.ATTR_CLASS.equalsIgnoreCase(attributes.getLocalName(i))) {\n\t\t\t\t\tlitePalAttr.addClassName(attributes.getValue(i).trim());\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (LitePalParser.NODE_CASES.equalsIgnoreCase(localName)) {\n\t\t\tfor (int i = 0; i < attributes.getLength(); i++) {\n\t\t\t\tif (LitePalParser.ATTR_VALUE.equalsIgnoreCase(attributes.getLocalName(i))) {\n\t\t\t\t\tlitePalAttr.setCases(attributes.getValue(i).trim());\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (LitePalParser.NODE_STORAGE.equalsIgnoreCase(localName)) {\n            for (int i = 0; i < attributes.getLength(); i++) {\n                if (LitePalParser.ATTR_VALUE.equalsIgnoreCase(attributes.getLocalName(i))) {\n                    litePalAttr.setStorage(attributes.getValue(i).trim());\n                }\n            }\n        }\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/parser/LitePalParser.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.parser;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.parsers.SAXParserFactory;\n\nimport org.litepal.LitePalApplication;\nimport org.litepal.exceptions.ParseConfigurationFileException;\nimport org.litepal.util.Const;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.XMLReader;\nimport org.xmlpull.v1.XmlPullParser;\nimport org.xmlpull.v1.XmlPullParserException;\nimport org.xmlpull.v1.XmlPullParserFactory;\n\nimport android.content.res.AssetManager;\nimport android.content.res.Resources.NotFoundException;\n\n/**\n * The class is used to parse the litepal.xml file. There're three usual ways to\n * parse XML in android, SAX, Pull and DOM. LitePal use SAX as default option,\n * and DOM parser will be added soon.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class LitePalParser {\n\n\t/**\n\t * Node name dbname.\n\t */\n\tstatic final String NODE_DB_NAME = \"dbname\";\n\n\t/**\n\t * Node name version.\n\t */\n\tstatic final String NODE_VERSION = \"version\";\n\n\t/**\n\t * Node name list. Currently not used.\n\t */\n\tstatic final String NODE_LIST = \"list\";\n\n\t/**\n\t * Node name mapping.\n\t */\n\tstatic final String NODE_MAPPING = \"mapping\";\n\n\t/**\n\t * Node name column case.\n\t */\n\tstatic final String NODE_CASES = \"cases\";\n\n    /**\n     * Node name column storage.\n     */\n    static final String NODE_STORAGE = \"storage\";\n\n\t/**\n\t * Attribute name value, for dbname and version node.\n\t */\n\tstatic final String ATTR_VALUE = \"value\";\n\n\t/**\n\t * Attribute name class, for mapping node.\n\t */\n\tstatic final String ATTR_CLASS = \"class\";\n\n\t/**\n\t * Store the parsed value of litepal.xml.\n\t */\n\tprivate static LitePalParser parser;\n\n\t/**\n\t * Analyze litepal.xml, and store the analyzed result in LitePalParser. Use\n\t * DomParse to parse the configuration file as default. SAXParser and\n\t * XmlPullParser is also optional, but not visible to developers.\n\t */\n\tpublic static LitePalConfig parseLitePalConfiguration() {\n\t\tif (parser == null) {\n\t\t\tparser = new LitePalParser();\n\t\t}\n\t\treturn parser.usePullParse();\n\t}\n\n\t/**\n\t * Use SAXParser to parse the litepal.xml file. It will get the parsed\n\t * result from LitePalContentHandler and stored in the instance of\n\t * LitePalAttr.\n\t * \n\t * Note while analyzing litepal.xml file, ParseConfigurationFileException\n\t * could be thrown. Be careful of writing litepal.xml file, or developer's\n\t * application may be crash.\n\t */\n    private void useSAXParser() {\n\t\tLitePalContentHandler handler;\n\t\ttry {\n\t\t\tSAXParserFactory factory = SAXParserFactory.newInstance();\n\t\t\tXMLReader xmlReader = factory.newSAXParser().getXMLReader();\n\t\t\thandler = new LitePalContentHandler();\n\t\t\txmlReader.setContentHandler(handler);\n\t\t\txmlReader.parse(new InputSource(getConfigInputStream()));\n\t\t} catch (NotFoundException e) {\n\t\t\tthrow new ParseConfigurationFileException(\n\t\t\t\t\tParseConfigurationFileException.CAN_NOT_FIND_LITEPAL_FILE);\n\t\t} catch (SAXException e) {\n\t\t\tthrow new ParseConfigurationFileException(\n\t\t\t\t\tParseConfigurationFileException.FILE_FORMAT_IS_NOT_CORRECT);\n\t\t} catch (ParserConfigurationException e) {\n\t\t\tthrow new ParseConfigurationFileException(\n\t\t\t\t\tParseConfigurationFileException.PARSE_CONFIG_FAILED);\n\t\t} catch (IOException e) {\n\t\t\tthrow new ParseConfigurationFileException(ParseConfigurationFileException.IO_EXCEPTION);\n\t\t}\n\t}\n\n\t/**\n\t * Use XmlPullParser to parse the litepal.xml file. It will store the result\n\t * in the instance of LitePalAttr.\n\t * \n\t * Note while analyzing litepal.xml file, ParseConfigurationFileException\n\t * could be thrown. Be careful of writing litepal.xml file, or developer's\n\t * application may be crash.\n\t */\n    private LitePalConfig usePullParse() {\n\t\ttry {\n\t\t\tLitePalConfig litePalConfig = new LitePalConfig();\n\t\t\tXmlPullParserFactory factory = XmlPullParserFactory.newInstance();\n\t\t\tXmlPullParser xmlPullParser = factory.newPullParser();\n\t\t\txmlPullParser.setInput(getConfigInputStream(), \"UTF-8\");\n\t\t\tint eventType = xmlPullParser.getEventType();\n\t\t\twhile (eventType != XmlPullParser.END_DOCUMENT) {\n\t\t\t\tString nodeName = xmlPullParser.getName();\n\t\t\t\tswitch (eventType) {\n\t\t\t\tcase XmlPullParser.START_TAG: {\n\t\t\t\t\tif (NODE_DB_NAME.equals(nodeName)) {\n\t\t\t\t\t\tString dbName = xmlPullParser.getAttributeValue(\"\", ATTR_VALUE);\n                        litePalConfig.setDbName(dbName);\n\t\t\t\t\t} else if (NODE_VERSION.equals(nodeName)) {\n\t\t\t\t\t\tString version = xmlPullParser.getAttributeValue(\"\", ATTR_VALUE);\n                        litePalConfig.setVersion(Integer.parseInt(version));\n\t\t\t\t\t} else if (NODE_MAPPING.equals(nodeName)) {\n\t\t\t\t\t\tString className = xmlPullParser.getAttributeValue(\"\", ATTR_CLASS);\n                        litePalConfig.addClassName(className);\n\t\t\t\t\t} else if (NODE_CASES.equals(nodeName)) {\n\t\t\t\t\t\tString cases = xmlPullParser.getAttributeValue(\"\", ATTR_VALUE);\n                        litePalConfig.setCases(cases);\n\t\t\t\t\t} else if (NODE_STORAGE.equals(nodeName)) {\n                        String storage = xmlPullParser.getAttributeValue(\"\", ATTR_VALUE);\n                        litePalConfig.setStorage(storage);\n                    }\n                    break;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\teventType = xmlPullParser.next();\n\t\t\t}\n            return litePalConfig;\n\t\t} catch (XmlPullParserException e) {\n\t\t\tthrow new ParseConfigurationFileException(\n\t\t\t\t\tParseConfigurationFileException.FILE_FORMAT_IS_NOT_CORRECT);\n\t\t} catch (IOException e) {\n\t\t\tthrow new ParseConfigurationFileException(ParseConfigurationFileException.IO_EXCEPTION);\n\t\t}\n\t}\n\n\t/**\n\t * Iterates all files in the root of assets folder. If find litepal.xml,\n\t * open this file and return the input stream. Or throw\n\t * ParseConfigurationFileException.\n\t * \n\t * @return The input stream of litepal.xml.\n\t * @throws java.io.IOException\n\t */\n\tprivate InputStream getConfigInputStream() throws IOException {\n\t\tAssetManager assetManager = LitePalApplication.getContext().getAssets();\n\t\tString[] fileNames = assetManager.list(\"\");\n\t\tif (fileNames != null && fileNames.length > 0) {\n\t\t\tfor (String fileName : fileNames) {\n\t\t\t\tif (Const.Config.CONFIGURATION_FILE_NAME.equalsIgnoreCase(fileName)) {\n\t\t\t\t\treturn assetManager.open(fileName, AssetManager.ACCESS_BUFFER);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow new ParseConfigurationFileException(\n\t\t\t\tParseConfigurationFileException.CAN_NOT_FIND_LITEPAL_FILE);\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/AssociationCreator.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.text.TextUtils;\n\nimport org.litepal.exceptions.DatabaseGenerateException;\nimport org.litepal.tablemanager.model.AssociationsModel;\nimport org.litepal.tablemanager.model.ColumnModel;\nimport org.litepal.tablemanager.model.GenericModel;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.Const;\nimport org.litepal.util.DBUtility;\nimport org.litepal.util.LitePalLog;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Locale;\n\n/**\n * When models have associations such as one2one, many2one or many2many, tables\n * should add foreign key column or create intermediate table to make the object\n * association mapping right. This process will be proceed automatically without\n * concerning by users. To make this happen, user just need to declare the\n * associations clearly in the models, and make sure all the mapping models are\n * added in the litepal.xml file.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic abstract class AssociationCreator extends Generator {\n\n\tprotected abstract void createOrUpgradeTable(SQLiteDatabase db, boolean force);\n\n\t/**\n\t * {@link org.litepal.tablemanager.AssociationCreator} analyzes two things. Add associations\n\t * including add foreign key column to tables and create intermediate join\n\t * tables.\n\t */\n\t@Override\n\tprotected void addOrUpdateAssociation(SQLiteDatabase db, boolean force) {\n\t\taddAssociations(getAllAssociations(), db, force);\n\t}\n\n\t/**\n\t * Generate a create table SQL by the passed in parameters. Note that it\n\t * will always generate a SQL with id/_id column in it as primary key and\n\t * this id is auto increment as integer if the autoIncrementId is true, or\n\t * no primary key will be added.\n\t * \n\t * @param tableName\n\t *            The table name.\n\t * @param columnModels\n\t *            A list contains all column models with column info.\n\t * @param autoIncrementId\n\t *            Generate an auto increment id or not. Only intermediate join table doesn't need\n     *            an auto increment id.\n\t * @return A generated create table SQL.\n\t */\n\tprotected String generateCreateTableSQL(String tableName, Collection<ColumnModel> columnModels,\n\t\t\tboolean autoIncrementId) {\n\t\tStringBuilder createTableSQL = new StringBuilder(\"create table \");\n\t\tcreateTableSQL.append(tableName).append(\" (\");\n\t\tif (autoIncrementId) {\n\t\t\tcreateTableSQL.append(\"id integer primary key autoincrement,\");\n\t\t}\n\t\tif (isContainsOnlyIdField(columnModels)) {\n            // Remove the last comma when only have id field in model.\n\t\t\tcreateTableSQL.deleteCharAt(createTableSQL.length() - 1);\n\t\t}\n\t\tboolean needSeparator = false;\n        for (ColumnModel columnModel : columnModels) {\n            if (columnModel.isIdColumn()) {\n                continue;\n            }\n            if (needSeparator) {\n                createTableSQL.append(\", \");\n            }\n            needSeparator = true;\n            createTableSQL.append(columnModel.getColumnName()).append(\" \").append(columnModel.getColumnType());\n            if (!columnModel.isNullable()) {\n                createTableSQL.append(\" not null\");\n            }\n            if (columnModel.isUnique()) {\n                createTableSQL.append(\" unique\");\n            }\n            String defaultValue = columnModel.getDefaultValue();\n            if (!TextUtils.isEmpty(defaultValue)) {\n                createTableSQL.append(\" default \").append(defaultValue);\n            }\n        }\n\t\tcreateTableSQL.append(\")\");\n\t\tLitePalLog.d(TAG, \"create table sql is >> \" + createTableSQL);\n\t\treturn createTableSQL.toString();\n\t}\n\n\t/**\n\t * Generate create index SQLs by the passed in parameters.\n\t *\n\t * @param tableName\n\t *            The table name.\n\t * @param columnModels\n\t *            A list contains all column models with column info.\n\t * @return A generated create index SQLs.\n\t */\n\tprotected List<String> generateCreateIndexSQLs(String tableName, Collection<ColumnModel> columnModels) {\n\t\tList<String> sqls = new ArrayList<>();\n\t\tfor (ColumnModel columnModel : columnModels) {\n\t\t\tif (columnModel.hasIndex()) {\n\t\t\t\tsqls.add(generateCreateIndexSQL(tableName, columnModel));\n\t\t\t}\n\t\t}\n\t\treturn sqls;\n\t}\n\n\t/**\n\t * Generate a SQL for dropping table.\n\t * \n\t * @param tableName\n\t *            The table name.\n\t * @return A SQL to drop table.\n\t */\n\tprotected String generateDropTableSQL(String tableName) {\n\t\treturn \"drop table if exists \" + tableName;\n\t}\n\n\t/**\n\t * Generate a SQL for add new column into the existing table.\n\t * @param tableName\n     *          The table which want to add a column\n\t * @param columnModel\n\t *          Which contains column info\n\t * @return A SQL to add new column.\n\t */\n\tprotected String generateAddColumnSQL(String tableName, ColumnModel columnModel) {\n\t\tStringBuilder addColumnSQL = new StringBuilder();\n\t\taddColumnSQL.append(\"alter table \").append(tableName);\n        addColumnSQL.append(\" add column \").append(columnModel.getColumnName());\n        addColumnSQL.append(\" \").append(columnModel.getColumnType());\n        if (!columnModel.isNullable()) {\n            addColumnSQL.append(\" not null\");\n        }\n        if (columnModel.isUnique()) {\n            addColumnSQL.append(\" unique\");\n        }\n        String defaultValue = columnModel.getDefaultValue();\n        if (!TextUtils.isEmpty(defaultValue)) {\n            addColumnSQL.append(\" default \").append(defaultValue);\n        } else {\n            if (!columnModel.isNullable()) {\n                if (\"integer\".equalsIgnoreCase(columnModel.getColumnType())) {\n                    defaultValue = \"0\";\n                } else if (\"text\".equalsIgnoreCase(columnModel.getColumnType())) {\n                    defaultValue = \"''\";\n                } else if (\"real\".equalsIgnoreCase(columnModel.getColumnType())) {\n                    defaultValue = \"0.0\";\n                }\n                addColumnSQL.append(\" default \").append(defaultValue);\n            }\n        }\n\t\tLitePalLog.d(TAG, \"add column sql is >> \" + addColumnSQL);\n\t\treturn addColumnSQL.toString();\n\t}\n\n\t/**\n\t * Generate create index SQL by the passed in parameters.\n\t *\n\t * @param tableName\n\t *            The table name.\n\t * @param columnModel\n\t *            Column model with column info.\n\t * @return A generated create index SQL.\n\t */\n\tprotected String generateCreateIndexSQL(String tableName, ColumnModel columnModel) {\n\t\tStringBuilder createIndexSQL = new StringBuilder();\n\t\tif (columnModel.hasIndex()) {\n\t\t\tcreateIndexSQL.append(\"create index \");\n\t\t\tcreateIndexSQL.append(DBUtility.getIndexName(tableName, columnModel.getColumnName()));\n\t\t\tcreateIndexSQL.append(\" on \");\n\t\t\tcreateIndexSQL.append(tableName);\n\t\t\tcreateIndexSQL.append(\" (\");\n\t\t\tcreateIndexSQL.append(columnModel.getColumnName());\n\t\t\tcreateIndexSQL.append(\")\");\n\t\t\tLitePalLog.d(TAG, \"create table index sql is >> \" + createIndexSQL);\n\t\t}\n\t\treturn createIndexSQL.toString();\n\t}\n\n\t/**\n\t * Judge the passed in column is a foreign key column format or not. Each\n\t * column name ends with _id will be considered as foreign key column\n\t * format.\n\t * \n\t * @param columnName\n\t *            The name of column.\n\t * @return Return true if it's foreign column format, otherwise return\n\t *         false.\n\t */\n\tprotected boolean isForeignKeyColumnFormat(String columnName) {\n\t\tif (!TextUtils.isEmpty(columnName)) {\n\t\t\treturn columnName.toLowerCase(Locale.US).endsWith(\"_id\") && !columnName.equalsIgnoreCase(\"_id\");\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Once there's new table created. The table name will be saved into\n\t * table_schema as a copy. Each table name will be saved only once.\n\t * \n\t * @param tableName\n\t *            The table name.\n\t * @param tableType\n\t *            0 means normal table, 1 means intermediate join table.\n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t */\n\tprotected void giveTableSchemaACopy(String tableName, int tableType, SQLiteDatabase db) {\n\t\tStringBuilder sql = new StringBuilder(\"select * from \");\n\t\tsql.append(Const.TableSchema.TABLE_NAME);\n\t\tLitePalLog.d(TAG, \"giveTableSchemaACopy SQL is >> \" + sql);\n\t\tCursor cursor = null;\n\t\ttry {\n\t\t\tcursor = db.rawQuery(sql.toString(), null);\n\t\t\tif (isNeedtoGiveACopy(cursor, tableName)) {\n\t\t\t\tContentValues values = new ContentValues();\n\t\t\t\tvalues.put(Const.TableSchema.COLUMN_NAME, BaseUtility.changeCase(tableName));\n\t\t\t\tvalues.put(Const.TableSchema.COLUMN_TYPE, tableType);\n\t\t\t\tdb.insert(Const.TableSchema.TABLE_NAME, null, values);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t} finally {\n\t\t\tif (cursor != null) {\n\t\t\t\tcursor.close();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Save the name of a created table into table_schema, but there're some\n\t * extra rules. Each table name should be only saved once, and special\n\t * tables will not be saved.\n\t * \n\t * @param cursor\n\t *            The cursor used to iterator values in the table.\n\t * @param tableName\n\t *            The table name.\n\t * @return If all rules are passed return true, any of them failed return\n\t *         false.\n\t */\n\tprivate boolean isNeedtoGiveACopy(Cursor cursor, String tableName) {\n\t\treturn !isValueExists(cursor, tableName) && !isSpecialTable(tableName);\n\t}\n\n\t/**\n\t * Judge the table name has already exist in the table_schema or not.\n\t * \n\t * @param cursor\n\t *            The cursor used to iterator values in the table.\n\t * @param tableName\n\t *            The table name.\n\t * @return If value exists return true, or return false.\n\t */\n\tprivate boolean isValueExists(Cursor cursor, String tableName) {\n\t\tboolean exist = false;\n\t\tif (cursor.moveToFirst()) {\n\t\t\tdo {\n\t\t\t\tString name = cursor.getString(cursor\n\t\t\t\t\t\t.getColumnIndexOrThrow(Const.TableSchema.COLUMN_NAME));\n\t\t\t\tif (name.equalsIgnoreCase(tableName)) {\n\t\t\t\t\texist = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} while (cursor.moveToNext());\n\t\t}\n\t\treturn exist;\n\t}\n\n\t/**\n\t * Judge a table is a special table or not. Currently table_schema is a\n\t * special table.\n\t * \n\t * @param tableName\n\t *            The table name.\n\t * @return Return true if it's special table.\n\t */\n\tprivate boolean isSpecialTable(String tableName) {\n\t\treturn Const.TableSchema.TABLE_NAME.equalsIgnoreCase(tableName);\n\t}\n\n\t/**\n\t * Analyzing all the association models in the collection. Judge their\n\t * association types. If it's one2one or many2one associations, add the\n\t * foreign key column to the associated table. If it's many2many\n\t * associations, create an intermediate join table.\n\t * \n\t * @param associatedModels\n\t *            A collection contains all the association models.Use the\n\t *            association models to get association type and associated\n\t *            table names.\n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @param force\n\t *            Drop the table first if it already exists.\n\t */\n\tprivate void addAssociations(Collection<AssociationsModel> associatedModels, SQLiteDatabase db,\n\t\t\tboolean force) {\n\t\tfor (AssociationsModel associationModel : associatedModels) {\n\t\t\tif (Const.Model.MANY_TO_ONE == associationModel.getAssociationType()\n\t\t\t\t\t|| Const.Model.ONE_TO_ONE == associationModel.getAssociationType()) {\n\t\t\t\taddForeignKeyColumn(associationModel.getTableName(),\n\t\t\t\t\t\tassociationModel.getAssociatedTableName(),\n\t\t\t\t\t\tassociationModel.getTableHoldsForeignKey(), db);\n\t\t\t} else if (Const.Model.MANY_TO_MANY == associationModel.getAssociationType()) {\n\t\t\t\tcreateIntermediateTable(associationModel.getTableName(),\n\t\t\t\t\t\tassociationModel.getAssociatedTableName(), db, force);\n\t\t\t}\n\t\t}\n        for (GenericModel genericModel : getGenericModels()) {\n            createGenericTable(genericModel, db, force);\n        }\n\t}\n\n\t/**\n\t * When it comes to many2many associations. Database need to create an\n\t * intermediate table for mapping this association. This method helps create\n\t * such a table, and the table name follows the concatenation of the two\n\t * target table names in alphabetical order with underline in the middle.\n\t * \n\t * @param tableName\n\t *            The table name.\n\t * @param associatedTableName\n\t *            The associated table name.\n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @param force\n\t *            Drop the table first if it already exists.\n\t */\n\tprivate void createIntermediateTable(String tableName, String associatedTableName,\n\t\t\tSQLiteDatabase db, boolean force) {\n        List<ColumnModel> columnModelList = new ArrayList<>();\n        ColumnModel column1 = new ColumnModel();\n        column1.setColumnName(tableName + \"_id\");\n        column1.setColumnType(\"integer\");\n        ColumnModel column2 = new ColumnModel();\n        column2.setColumnName(associatedTableName + \"_id\");\n        column2.setColumnType(\"integer\");\n        columnModelList.add(column1);\n        columnModelList.add(column2);\n        String intermediateTableName = DBUtility.getIntermediateTableName(tableName,\n                associatedTableName);\n\t\tList<String> sqls = new ArrayList<>();\n\t\tif (DBUtility.isTableExists(intermediateTableName, db)) {\n\t\t\tif (force) {\n\t\t\t\tsqls.add(generateDropTableSQL(intermediateTableName));\n\t\t\t\tsqls.add(generateCreateTableSQL(intermediateTableName, columnModelList, false));\n\t\t\t}\n\t\t} else {\n\t\t\tsqls.add(generateCreateTableSQL(intermediateTableName, columnModelList, false));\n\t\t}\n\t\texecute(sqls, db);\n\t\tgiveTableSchemaACopy(intermediateTableName, Const.TableSchema.INTERMEDIATE_JOIN_TABLE, db);\n\t}\n\n    /**\n     * When declared generic collection fields in model class. Database need to create\n     * generic tables for mapping these fields. This method helps create such a table.\n     *\n     * @param genericModel\n     *          The GenericModel instance.\n     * @param db\n     *          Instance of SQLiteDatabase.\n     * @param force\n     *          Drop the table first if it already exists.\n     */\n    private void createGenericTable(GenericModel genericModel, SQLiteDatabase db, boolean force) {\n        String tableName = genericModel.getTableName();\n        String valueColumnName = genericModel.getValueColumnName();\n        String valueColumnType = genericModel.getValueColumnType();\n        String valueIdColumnName = genericModel.getValueIdColumnName();\n        List<ColumnModel> columnModelList = new ArrayList<>();\n        ColumnModel column1 = new ColumnModel();\n        column1.setColumnName(valueColumnName);\n        column1.setColumnType(valueColumnType);\n        ColumnModel column2 = new ColumnModel();\n        column2.setColumnName(valueIdColumnName);\n        column2.setColumnType(\"integer\");\n        columnModelList.add(column1);\n        columnModelList.add(column2);\n        List<String> sqls = new ArrayList<>();\n        if (DBUtility.isTableExists(tableName, db)) {\n            if (force) {\n                sqls.add(generateDropTableSQL(tableName));\n                sqls.add(generateCreateTableSQL(tableName, columnModelList, false));\n            }\n        } else {\n            sqls.add(generateCreateTableSQL(tableName, columnModelList, false));\n        }\n        execute(sqls, db);\n        giveTableSchemaACopy(tableName, Const.TableSchema.GENERIC_TABLE, db);\n    }\n\n\t/**\n\t * This method is used to add many to one association or one to one\n\t * association on tables. It will automatically build a SQL to add foreign\n\t * key to a table. If the passed in table name or associated table name\n\t * doesn't exist, it will throw an exception.\n\t * \n\t * @param tableName\n\t *            The table name.\n\t * @param associatedTableName\n\t *            The associated table name.\n\t * @param tableHoldsForeignKey\n\t *            The table which holds the foreign key.\n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t */\n\tprotected void addForeignKeyColumn(String tableName, String associatedTableName,\n\t\t\tString tableHoldsForeignKey, SQLiteDatabase db) {\n\t\tif (DBUtility.isTableExists(tableName, db)) {\n\t\t\tif (DBUtility.isTableExists(associatedTableName, db)) {\n\t\t\t\tString foreignKeyColumn = null;\n\t\t\t\tif (tableName.equals(tableHoldsForeignKey)) {\n\t\t\t\t\tforeignKeyColumn = getForeignKeyColumnName(associatedTableName);\n\t\t\t\t} else if (associatedTableName.equals(tableHoldsForeignKey)) {\n\t\t\t\t\tforeignKeyColumn = getForeignKeyColumnName(tableName);\n\t\t\t\t}\n\t\t\t\tif (!DBUtility.isColumnExists(foreignKeyColumn, tableHoldsForeignKey, db)) {\n                    ColumnModel columnModel = new ColumnModel();\n                    columnModel.setColumnName(foreignKeyColumn);\n                    columnModel.setColumnType(\"integer\");\n                    List<String> sqls = new ArrayList<>();\n                    sqls.add(generateAddColumnSQL(tableHoldsForeignKey, columnModel));\n\t\t\t\t\texecute(sqls, db);\n\t\t\t\t} else {\n\t\t\t\t\tLitePalLog.d(TAG, \"column \" + foreignKeyColumn\n\t\t\t\t\t\t\t+ \" is already exist, no need to add one\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new DatabaseGenerateException(DatabaseGenerateException.TABLE_DOES_NOT_EXIST\n\t\t\t\t\t\t+ associatedTableName);\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new DatabaseGenerateException(DatabaseGenerateException.TABLE_DOES_NOT_EXIST\n\t\t\t\t\t+ tableName);\n\t\t}\n\t}\n\n    /**\n     * Check if the ColumnModel list contains only id field.\n     * @param columnModels\n     *          List contains model fields.\n     * @return If ColumnModel list is empty or contains only id, _id field, return true. Otherwise return false.\n     */\n    private boolean isContainsOnlyIdField(Collection<ColumnModel> columnModels) {\n    \tfor (ColumnModel columnModel : columnModels) {\n    \t\tif (!columnModel.isIdColumn()) return false;\n\t\t}\n    \treturn true;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/AssociationUpdater.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.litepal.parser.LitePalAttr;\nimport org.litepal.tablemanager.model.AssociationsModel;\nimport org.litepal.tablemanager.model.ColumnModel;\nimport org.litepal.tablemanager.model.GenericModel;\nimport org.litepal.tablemanager.model.TableModel;\nimport org.litepal.util.Const;\nimport org.litepal.util.DBUtility;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.LitePalLog;\n\nimport android.database.sqlite.SQLiteDatabase;\n\n/**\n * Upgrade the associations between model classes into tables. Creating new\n * tables and adding new foreign key columns are done in\n * {@link org.litepal.tablemanager.AssociationUpdater}. So this class just deal with the simple job of\n * removing foreign key columns and dropping dump intermediate join tables.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic abstract class AssociationUpdater extends Creator {\n\n\tpublic static final String TAG = \"AssociationUpdater\";\n\n\t/**\n\t * A collection contains all the association models.\n\t */\n\tprivate Collection<AssociationsModel> mAssociationModels;\n\n\t/**\n\t * Instance of SQLiteDatabase.\n\t */\n\tprotected SQLiteDatabase mDb;\n\n\t/**\n\t * Analysis the {@link org.litepal.tablemanager.model.TableModel} by the purpose of subclasses, and\n\t * generate a SQL to do the intention job. The implementation of this method\n\t * is totally delegated to the subclasses.\n\t */\n\t@Override\n\tprotected abstract void createOrUpgradeTable(SQLiteDatabase db, boolean force);\n\n\t/**\n\t * {@link org.litepal.tablemanager.AssociationUpdater} does two jobs. Removing foreign key columns\n\t * when two models are not associated anymore, and remove the intermediate\n\t * join tables when two models are not associated anymore.\n\t */\n\t@Override\n\tprotected void addOrUpdateAssociation(SQLiteDatabase db, boolean force) {\n\t\tmAssociationModels = getAllAssociations();\n\t\tmDb = db;\n\t\tremoveAssociations();\n\t}\n\n\t/**\n\t * This method looks around all the columns in the table, and judge which of\n\t * them are foreign key columns.\n\t * \n\t * @param tableModel\n\t *            Use the TableModel to get table name and columns name to\n\t *            generate SQL.\n\t * @return All the foreign key columns in a list.\n\t */\n\tprotected List<String> getForeignKeyColumns(TableModel tableModel) {\n\t\tList<String> foreignKeyColumns = new ArrayList<>();\n        Collection<ColumnModel> columnModels = getTableModelFromDB(tableModel.getTableName()).getColumnModels();\n\t\tfor (ColumnModel columnModel : columnModels) {\n            String columnName = columnModel.getColumnName();\n\t\t\tif (isForeignKeyColumnFormat(columnModel.getColumnName())) {\n                if (!tableModel.containsColumn(columnName)) {\n                    // Now this is a foreign key column.\n                    LitePalLog.d(TAG, \"getForeignKeyColumnNames >> foreign key column is \" + columnName);\n                    foreignKeyColumns.add(columnName);\n                }\n\t\t\t}\n\t\t}\n\t\treturn foreignKeyColumns;\n\t}\n\n\t/**\n\t * Judge the passed in column is a foreign key column or not. Each column\n\t * name ends with _id will be considered as foreign key column.\n\t * \n\t * @param tableModel\n\t *            Use the TableModel to get table name and columns name to\n\t *            generate SQL.\n\t * @param columnName\n\t *            The column to judge.\n\t * @return Return true if it's foreign column, otherwise return false.\n\t */\n\tprotected boolean isForeignKeyColumn(TableModel tableModel, String columnName) {\n\t\treturn BaseUtility.containsIgnoreCases(getForeignKeyColumns(tableModel), columnName);\n\t}\n\n\t/**\n\t * Look from the database to find a table named same as the table name in\n\t * table model. Then iterate the columns and types of this table to create a\n\t * new instance of table model. If there's no such a table in the database,\n\t * then throw DatabaseGenerateException.\n\t * \n\t * @param tableName\n\t *            The table name use to get table model from database.\n\t * @return A table model object with values from database table.\n\t */\n\tprotected TableModel getTableModelFromDB(String tableName) {\n\t\treturn DBUtility.findPragmaTableInfo(tableName, mDb);\n\t}\n\n\t/**\n\t * Drop the tables by the passing table name.\n\t * \n\t * @param dropTableNames\n\t *            The names of the tables that need to drop.\n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t */\n\tprotected void dropTables(List<String> dropTableNames, SQLiteDatabase db) {\n\t\tif (dropTableNames != null && !dropTableNames.isEmpty()) {\n            List<String> dropTableSQLS = new ArrayList<>();\n\t\t\tfor (int i = 0; i < dropTableNames.size(); i++) {\n                dropTableSQLS.add(generateDropTableSQL(dropTableNames.get(i)));\n\t\t\t}\n\t\t\texecute(dropTableSQLS, db);\n\t\t}\n\t}\n\n\t/**\n\t * When some fields are removed from class, the table should synchronize the\n\t * changes by removing the corresponding columns.\n\t * \n\t * @param removeColumnNames\n\t *            The column names that need to remove.\n\t * @param tableName\n\t *            The table name to remove columns from.\n\t */\n\tprotected void removeColumns(Collection<String> removeColumnNames, String tableName) {\n\t\tif (removeColumnNames != null && !removeColumnNames.isEmpty()) {\n\t\t\texecute(getRemoveColumnSQLs(removeColumnNames, tableName), mDb);\n\t\t}\n\t}\n\n\t/**\n\t * The values in table_schame should be synchronized with the model tables\n\t * in the database. If a model table is dropped, the corresponding data\n\t * should be removed from table_schema too.\n\t * \n\t * @param tableNames\n\t *            The table names need to remove from table_schema.\n\t */\n\tprotected void clearCopyInTableSchema(List<String> tableNames) {\n\t\tif (tableNames != null && !tableNames.isEmpty()) {\n\t\t\tStringBuilder deleteData = new StringBuilder(\"delete from \");\n\t\t\tdeleteData.append(Const.TableSchema.TABLE_NAME).append(\" where\");\n\t\t\tboolean needOr = false;\n\t\t\tfor (String tableName : tableNames) {\n\t\t\t\tif (needOr) {\n\t\t\t\t\tdeleteData.append(\" or \");\n\t\t\t\t}\n\t\t\t\tneedOr = true;\n\t\t\t\tdeleteData.append(\" lower(\").append(Const.TableSchema.COLUMN_NAME).append(\") \");\n\t\t\t\tdeleteData.append(\"=\").append(\" lower('\").append(tableName).append(\"')\");\n\t\t\t}\n\t\t\tLitePalLog.d(TAG, \"clear table schema value sql is \" + deleteData);\n            List<String> sqls = new ArrayList<>();\n            sqls.add(deleteData.toString());\n\t\t\texecute(sqls, mDb);\n\t\t}\n\t}\n\n\t/**\n\t * When the association between two tables are no longer associated in the\n\t * classes, database should remove the foreign key column or intermediate\n\t * join table that keeps these two tables associated.\n\t */\n\tprivate void removeAssociations() {\n\t\tremoveForeignKeyColumns();\n\t\tremoveIntermediateTables();\n        removeGenericTables();\n\t}\n\n\t/**\n\t * Analyzing the table models, then remove all the foreign key columns if\n\t * their association in model classes are no longer exist any more.\n\t */\n\tprivate void removeForeignKeyColumns() {\n\t\tfor (String className : LitePalAttr.getInstance().getClassNames()) {\n\t\t\tTableModel tableModel = getTableModel(className);\n\t\t\tremoveColumns(findForeignKeyToRemove(tableModel), tableModel.getTableName());\n\t\t}\n\t}\n\n\t/**\n\t * If there're intermediate join tables for two tables, when the two classes\n\t * are not associated, the join table should be dropped.\n\t */\n\tprivate void removeIntermediateTables() {\n\t\tList<String> tableNamesToDrop = findIntermediateTablesToDrop();\n\t\tdropTables(tableNamesToDrop, mDb);\n\t\tclearCopyInTableSchema(tableNamesToDrop);\n\t}\n\n    /**\n     * If there're generic tables for generic fields, when the fields are removed\n     * from class, the generic tables should be dropped.\n     */\n    private void removeGenericTables() {\n        List<String> tableNamesToDrop = findGenericTablesToDrop();\n        dropTables(tableNamesToDrop, mDb);\n        clearCopyInTableSchema(tableNamesToDrop);\n    }\n\n\t/**\n\t * This method gives back the names of the foreign key columns that need to\n\t * remove, cause their associations in the classes are no longer exist.\n\t * \n\t * @param tableModel\n\t *            Use the TableModel to get table name and columns name to\n\t *            generate SQL.\n\t * @return The foreign key columns need to remove in a list.\n\t */\n\tprivate List<String> findForeignKeyToRemove(TableModel tableModel) {\n\t\tList<String> removeRelations = new ArrayList<>();\n\t\tList<String> foreignKeyColumns = getForeignKeyColumns(tableModel);\n\t\tString selfTableName = tableModel.getTableName();\n\t\tfor (String foreignKeyColumn : foreignKeyColumns) {\n\t\t\tString associatedTableName = DBUtility.getTableNameByForeignColumn(foreignKeyColumn);\n\t\t\tif (shouldDropForeignKey(selfTableName, associatedTableName)) {\n\t\t\t\tremoveRelations.add(foreignKeyColumn);\n\t\t\t}\n\t\t}\n\t\tLitePalLog.d(TAG, \"findForeignKeyToRemove >> \" + tableModel.getTableName() + \" \"\n\t\t\t\t+ removeRelations);\n\t\treturn removeRelations;\n\t}\n\n\t/**\n\t * When many2many associations are no longer exist between two models, the\n\t * intermediate join table should be dropped from database. This method\n\t * helps find out those intermediate join tables which should be dropped\n\t * cause their associations in classes are done.\n\t * \n\t * @return A list with all intermediate join tables to drop.\n\t */\n\tprivate List<String> findIntermediateTablesToDrop() {\n\t\tList<String> intermediateTables = new ArrayList<>();\n\t\tfor (String tableName : DBUtility.findAllTableNames(mDb)) {\n\t\t\tif (DBUtility.isIntermediateTable(tableName, mDb)) {\n\t\t\t\tboolean dropIntermediateTable = true;\n\t\t\t\tfor (AssociationsModel associationModel : mAssociationModels) {\n\t\t\t\t\tif (associationModel.getAssociationType() == Const.Model.MANY_TO_MANY) {\n\t\t\t\t\t\tString intermediateTableName = DBUtility.getIntermediateTableName(\n\t\t\t\t\t\t\t\tassociationModel.getTableName(),\n\t\t\t\t\t\t\t\tassociationModel.getAssociatedTableName());\n\t\t\t\t\t\tif (tableName.equalsIgnoreCase(intermediateTableName)) {\n\t\t\t\t\t\t\tdropIntermediateTable = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (dropIntermediateTable) {\n\t\t\t\t\t// drop the intermediate join table\n\t\t\t\t\tintermediateTables.add(tableName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tLitePalLog.d(TAG, \"findIntermediateTablesToDrop >> \" + intermediateTables);\n\t\treturn intermediateTables;\n\t}\n\n    /**\n     * When generic fields are no longer exist in the class models, the generic tables should be\n     * dropped from database. This method helps find out those generic tables which should be dropped\n     * cause their generic fields in classes are removed.\n     *\n     * @return A list with all generic tables to drop.\n     */\n    private List<String> findGenericTablesToDrop() {\n        List<String> genericTablesToDrop = new ArrayList<>();\n        for (String tableName : DBUtility.findAllTableNames(mDb)) {\n            if (DBUtility.isGenericTable(tableName, mDb)) {\n                boolean dropGenericTable = true;\n                for (GenericModel genericModel : getGenericModels()) {\n                    String genericTableName = genericModel.getTableName();\n\t\t\t\t\tif (tableName.equalsIgnoreCase(genericTableName)) {\n\t\t\t\t\t\tdropGenericTable = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n                }\n                if (dropGenericTable) {\n                    // drop the generic table\n                    genericTablesToDrop.add(tableName);\n                }\n            }\n        }\n        return genericTablesToDrop;\n    }\n\n\t/**\n\t * Generate a SQL for renaming the table into a temporary table.\n\t * \n\t * @param tableName\n\t *            The table name use to alter to temporary table.\n\t * @return SQL to rename table.\n\t */\n\tprotected String generateAlterToTempTableSQL(String tableName) {\n\t\tStringBuilder sql = new StringBuilder();\n\t\tsql.append(\"alter table \").append(tableName).append(\" rename to \")\n\t\t\t\t.append(getTempTableName(tableName));\n\t\treturn sql.toString();\n\t}\n\n\t/**\n\t * Generate a SQL to create new table by the table model from database. Also\n\t * it will remove the columns that need to remove before generating the SQL.\n\t * \n\t * @param removeColumnNames\n\t *            The column names need to remove.\n\t * @param tableModel\n\t *            Which contains table name use to create new table.\n\t * @return SQL to create new table.\n\t */\n\tprivate String generateCreateNewTableSQL(Collection<String> removeColumnNames, TableModel tableModel) {\n\t\tfor (String removeColumnName : removeColumnNames) {\n            tableModel.removeColumnModelByName(removeColumnName);\n\t\t}\n\t\treturn generateCreateTableSQL(tableModel);\n\t}\n\n\t/**\n\t * Generate a SQL to do the data migration job to avoid losing data.\n\t *\n\t * @param tableModel\n\t *            Which contains table name use to migrate data.\n\t * @return SQL to migrate data.\n\t */\n\tprotected String generateDataMigrationSQL(TableModel tableModel) {\n        String tableName = tableModel.getTableName();\n\t\tCollection<ColumnModel> columnModels = tableModel.getColumnModels();\n\t\tif (!columnModels.isEmpty()) {\n\t\t\tStringBuilder sql = new StringBuilder();\n\t\t\tsql.append(\"insert into \").append(tableName).append(\"(\");\n\t\t\tboolean needComma = false;\n\t\t\tfor (ColumnModel columnModel : columnModels) {\n\t\t\t\tif (needComma) {\n\t\t\t\t\tsql.append(\", \");\n\t\t\t\t}\n\t\t\t\tneedComma = true;\n\t\t\t\tsql.append(columnModel.getColumnName());\n\t\t\t}\n\t\t\tsql.append(\") \");\n\t\t\tsql.append(\"select \");\n\t\t\tneedComma = false;\n\t\t\tfor (ColumnModel columnModel : columnModels) {\n\t\t\t\tif (needComma) {\n\t\t\t\t\tsql.append(\", \");\n\t\t\t\t}\n\t\t\t\tneedComma = true;\n\t\t\t\tsql.append(columnModel.getColumnName());\n\t\t\t}\n\t\t\tsql.append(\" from \").append(getTempTableName(tableName));\n\t\t\treturn sql.toString();\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Generate a SQL to drop the temporary table.\n\t * \n\t * @param tableName\n\t *            The table name use to drop temporary table.\n\t * @return SQL to drop the temporary table.\n\t */\n\tprotected String generateDropTempTableSQL(String tableName) {\n\t\treturn generateDropTableSQL(getTempTableName(tableName));\n\t}\n\n\t/**\n\t * Removing or resizing columns from tables must need a temporary table to\n\t * store data, and here's the table name.\n\t * \n\t * @param tableName\n\t *            The table name use to generate temporary table name.\n\t * @return Temporary table name\n\t */\n\tprotected String getTempTableName(String tableName) {\n\t\treturn tableName + \"_temp\";\n\t}\n\n\t/**\n\t * This method create a SQL array for the whole remove dump columns job.\n\t * \n\t * @param removeColumnNames\n\t *            The column names need to remove.\n\t * @param tableName\n\t *            The table name to remove from.\n\t * @return A SQL list contains create temporary table, create new table,\n\t *         migrate data and drop temporary table.\n\t */\n\tprivate List<String> getRemoveColumnSQLs(Collection<String> removeColumnNames, String tableName) {\n        TableModel tableModelFromDB = getTableModelFromDB(tableName);\n\t\tString alterToTempTableSQL = generateAlterToTempTableSQL(tableName);\n\t\tLitePalLog.d(TAG, \"generateRemoveColumnSQL >> \" + alterToTempTableSQL);\n\t\tString createNewTableSQL = generateCreateNewTableSQL(removeColumnNames, tableModelFromDB);\n\t\tLitePalLog.d(TAG, \"generateRemoveColumnSQL >> \" + createNewTableSQL);\n\t\tString dataMigrationSQL = generateDataMigrationSQL(tableModelFromDB);\n\t\tLitePalLog.d(TAG, \"generateRemoveColumnSQL >> \" + dataMigrationSQL);\n\t\tString dropTempTableSQL = generateDropTempTableSQL(tableName);\n\t\tLitePalLog.d(TAG, \"generateRemoveColumnSQL >> \" + dropTempTableSQL);\n\t\tList<String> createIndexSQLs = generateCreateIndexSQLs(tableModelFromDB);\n        List<String> sqls = new ArrayList<>();\n        sqls.add(alterToTempTableSQL);\n        sqls.add(createNewTableSQL);\n        sqls.add(dataMigrationSQL);\n        sqls.add(dropTempTableSQL);\n        sqls.addAll(createIndexSQLs);\n\t\treturn sqls;\n\t}\n\n\t/**\n\t * Judge if the current iterated foreign key column should be dropped. It is\n\t * only used in {@link #findForeignKeyToRemove(org.litepal.tablemanager.model.TableModel)} when iterating\n\t * the foreign key column list. When this foreign key can not be found in\n\t * the association model collection, this foreign key should be dropped.\n\t * \n\t * @param selfTableName\n\t *            The table name of currently table model.\n\t * @param associatedTableName\n\t *            The associated table name of current table model.\n\t * @return If the foreign key currently iterated should be dropped, return\n\t *         true. Otherwise return false.\n\t */\n\tprivate boolean shouldDropForeignKey(String selfTableName, String associatedTableName) {\n\t\tfor (AssociationsModel associationModel : mAssociationModels) {\n\t\t\tif (associationModel.getAssociationType() == Const.Model.ONE_TO_ONE) {\n\t\t\t\tif (selfTableName.equalsIgnoreCase(associationModel.getTableHoldsForeignKey())) {\n\t\t\t\t\tif (associationModel.getTableName().equalsIgnoreCase(selfTableName)) {\n\t\t\t\t\t\tif (isRelationCorrect(associationModel, selfTableName, associatedTableName)) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (associationModel.getAssociatedTableName().equalsIgnoreCase(\n\t\t\t\t\t\t\tselfTableName)) {\n\t\t\t\t\t\tif (isRelationCorrect(associationModel, associatedTableName, selfTableName)) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (associationModel.getAssociationType() == Const.Model.MANY_TO_ONE) {\n\t\t\t\tif (isRelationCorrect(associationModel, associatedTableName, selfTableName)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Judge if the tableName1 equals {@link org.litepal.tablemanager.model.AssociationsModel#getTableName()}\n\t * and tableName2 equals {@link org.litepal.tablemanager.model.AssociationsModel#getAssociatedTableName()}.\n\t * \n\t * @param associationModel\n\t *            The association model to get table name and associated table\n\t *            name.\n\t * @param tableName1\n\t *            The table name to match table name from association model.\n\t * @param tableName2\n\t *            The table name to match associated table name from association\n\t *            model.\n\t * @return Return true if the description is true, otherwise return false.\n\t */\n\tprivate boolean isRelationCorrect(AssociationsModel associationModel, String tableName1,\n\t\t\tString tableName2) {\n\t\treturn associationModel.getTableName().equalsIgnoreCase(tableName1)\n\t\t\t\t&& associationModel.getAssociatedTableName().equalsIgnoreCase(tableName2);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/Connector.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager;\n\nimport android.database.sqlite.SQLiteDatabase;\nimport android.os.Environment;\nimport android.text.TextUtils;\n\nimport org.litepal.LitePalApplication;\nimport org.litepal.parser.LitePalAttr;\n\nimport java.io.File;\n\n/**\n * The connector to connect database provided by LitePal. Users can use this\n * class to get the instance of SQLiteDatabase. But users still need to write\n * their own CRUD logic by the returned SQLiteDatabase. It will be improved in\n * the future.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class Connector {\n\n\t/**\n\t * The quote of LitePalHelper.\n\t */\n\tprivate static LitePalOpenHelper mLitePalHelper;\n\n\t/**\n\t * Get a writable SQLiteDatabase.\n\t * \n\t * There're a lot of ways to operate database in android. But LitePal\n\t * doesn't support using ContentProvider currently. The best way to use\n\t * LitePal well is get the SQLiteDatabase instance and use the methods like\n\t * SQLiteDatabase#save, SQLiteDatabase#update, SQLiteDatabase#delete,\n\t * SQLiteDatabase#query in the SQLiteDatabase class to do the database\n\t * operation. It will be improved in the future.\n\t * \n\t * @return A writable SQLiteDatabase instance\n\t */\n\tpublic synchronized static SQLiteDatabase getWritableDatabase() {\n\t\tLitePalOpenHelper litePalHelper = buildConnection();\n\t\treturn litePalHelper.getWritableDatabase();\n\t}\n\n\t/**\n\t * Call getDatabase directly will invoke the getWritableDatabase method by\n\t * default.\n\t * \n\t * This is method is alias of getWritableDatabase.\n\t * \n\t * @return A writable SQLiteDatabase instance\n\t */\n\tpublic static SQLiteDatabase getDatabase() {\n\t\treturn getWritableDatabase();\n\t}\n\n\t/**\n\t * Build a connection to the database. This progress will analysis the\n\t * litepal.xml file, and will check if the fields in LitePalAttr are valid,\n\t * and it will open a SQLiteOpenHelper to decide to create tables or update\n\t * tables or doing nothing depends on the version attributes.\n\t * \n\t * After all the stuffs above are finished. This method will return a\n\t * LitePalHelper object.Notes this method could throw a lot of exceptions.\n\t * \n\t * @return LitePalHelper object.\n\t */\n\tprivate static LitePalOpenHelper buildConnection() {\n\t\tLitePalAttr litePalAttr = LitePalAttr.getInstance();\n\t\tlitePalAttr.checkSelfValid();\n\t\tif (mLitePalHelper == null) {\n\t\t\tString dbName = litePalAttr.getDbName();\n\t\t\tif (\"external\".equalsIgnoreCase(litePalAttr.getStorage())) {\n\t\t\t\tdbName = LitePalApplication.getContext().getExternalFilesDir(\"\") + \"/databases/\" + dbName;\n\t\t\t} else if (!\"internal\".equalsIgnoreCase(litePalAttr.getStorage()) && !TextUtils.isEmpty(litePalAttr.getStorage())) {\n                // internal or empty means internal storage, neither or them means sdcard storage\n                String dbPath = Environment.getExternalStorageDirectory().getPath() + \"/\" + litePalAttr.getStorage();\n                dbPath = dbPath.replace(\"//\", \"/\");\n                File path = new File(dbPath);\n                if (!path.exists()) {\n                    path.mkdirs();\n                }\n                dbName = dbPath + \"/\" + dbName;\n            }\n\t\t\tmLitePalHelper = new LitePalOpenHelper(dbName, litePalAttr.getVersion());\n\t\t}\n\t\treturn mLitePalHelper;\n\t}\n\n\t/**\n\t * Never call this method. This is only used by internal.\n\t */\n\tpublic static void clearLitePalOpenHelperInstance() {\n        if (mLitePalHelper != null) {\n            mLitePalHelper.getWritableDatabase().close();\n            mLitePalHelper = null;\n        }\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/Creator.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager;\n\nimport org.litepal.tablemanager.model.TableModel;\nimport org.litepal.util.Const;\nimport org.litepal.util.DBUtility;\n\nimport android.database.sqlite.SQLiteDatabase;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * This is a subclass of Generator. Use to create tables. It will automatically\n * build a create table SQL based on the passing TableModel object. In case of\n * there's already a table with the same name in the database, LitePal will\n * always drop the table first before create a new one. If there's syntax error\n * in the executing SQL by accident, Creator will throw a\n * DatabaseGenerateException.\n * \n * @author Tony Green\n * @since 1.0\n */\nclass Creator extends AssociationCreator {\n\tpublic static final String TAG = \"Creator\";\n\n\t/**\n\t * Analyzing the table model, create a table in the database based on the\n\t * table model's value.\n\t */\n\t@Override\n\tprotected void createOrUpgradeTable(SQLiteDatabase db, boolean force) {\n\t\tfor (TableModel tableModel : getAllTableModels()) {\n\t\t\tcreateOrUpgradeTable(tableModel, db, force);\n\t\t}\n\t}\n\n    protected void createOrUpgradeTable(TableModel tableModel, SQLiteDatabase db, boolean force) {\n        execute(getCreateTableSQLs(tableModel, db, force), db);\n        giveTableSchemaACopy(tableModel.getTableName(), Const.TableSchema.NORMAL_TABLE, db);\n    }\n\n\t/**\n\t * When creating a new table, it should always try to drop the same name\n\t * table if exists. This method create a SQL array for the whole create\n\t * table job.\n\t * \n\t * @param tableModel\n\t *            The table model.\n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @param force\n\t *            Drop the table first if it already exists.\n\t * @return A SQL array contains drop table if it exists and create new\n\t *         table.\n\t */\n\tprotected List<String> getCreateTableSQLs(TableModel tableModel, SQLiteDatabase db, boolean force) {\n        List<String> sqls = new ArrayList<>();\n\t\tif (force) {\n            sqls.add(generateDropTableSQL(tableModel));\n            sqls.add(generateCreateTableSQL(tableModel));\n\t\t} else {\n\t\t\tif (DBUtility.isTableExists(tableModel.getTableName(), db)) {\n\t\t\t\treturn null;\n\t\t\t} else {\n                sqls.add(generateCreateTableSQL(tableModel));\n\t\t\t}\n\t\t}\n\t\tsqls.addAll(generateCreateIndexSQLs(tableModel)); // create index after create table\n        return sqls;\n\t}\n\n\t/**\n\t * Generate a SQL for dropping table.\n\t * \n\t * @param tableModel\n\t *            The table model.\n\t * @return A SQL to drop table.\n\t */\n    private String generateDropTableSQL(TableModel tableModel) {\n\t\treturn generateDropTableSQL(tableModel.getTableName());\n\t}\n\n\t/**\n\t * Generate a create table SQL by analyzing the TableModel. Note that it\n\t * will always generate a SQL with id/_id column in it as primary key, and\n\t * this id is auto increment as integer. Do not try to assign or modify it.\n\t * \n\t * @param tableModel\n\t *            Use the TableModel to get table name and columns name to\n\t *            generate SQL.\n\t * @return A generated create table SQL.\n\t */\n    String generateCreateTableSQL(TableModel tableModel) {\n\t\treturn generateCreateTableSQL(tableModel.getTableName(), tableModel.getColumnModels(), true);\n\t}\n\n\t/**\n\t * Generate create index SQLs by analyzing the TableModel.\n\t *\n\t * @param tableModel\n\t *            Use the TableModel to get table name and columns name to\n\t *            generate SQLs.\n\t * @return A generated create index SQLs.\n\t */\n\tList<String> generateCreateIndexSQLs(TableModel tableModel) {\n\t\treturn generateCreateIndexSQLs(tableModel.getTableName(), tableModel.getColumnModels());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/Dropper.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.litepal.tablemanager.model.TableModel;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.Const;\nimport org.litepal.util.LitePalLog;\n\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\n\n/**\n * When developers defined some model classes and define them in the mapping\n * list. All corresponding tables will be created automatically. But developers\n * might realize some model classes are useless or can be optimized to remove,\n * and they somehow drop the model classes. If the tables are still in the\n * database, it will be a mess soon. So this class helps do the dropping job.\n * Keep developers' database synchronized and clean.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class Dropper extends AssociationUpdater {\n\n\t/**\n\t * Use the TableModel to get table name and columns name to generate SQL.\n\t */\n\tprivate Collection<TableModel> mTableModels;\n\n\t/**\n\t * Analyzing the table model, to see which tables has no model classes\n\t * anymore and can be dropped.\n\t */\n\t@Override\n\tprotected void createOrUpgradeTable(SQLiteDatabase db, boolean force) {\n\t\tmTableModels = getAllTableModels();\n\t\tmDb = db;\n\t\tdropTables();\n\t}\n\n\t/**\n\t * Drop the tables which are not exist in the mapping list to keep\n\t * synchronization.\n\t */\n\tprivate void dropTables() {\n\t\tList<String> tableNamesToDrop = findTablesToDrop();\n\t\tdropTables(tableNamesToDrop, mDb);\n\t\tclearCopyInTableSchema(tableNamesToDrop);\n\t}\n\n\t/**\n\t * It will find all the tables need to drop in the database, following the\n\t * rules of {@link #shouldDropThisTable(String, int)}.\n\t * \n\t * @return A list contains all the table names need to drop.\n\t */\n\tprivate List<String> findTablesToDrop() {\n\t\tList<String> dropTableNames = new ArrayList<>();\n\t\tCursor cursor = null;\n\t\ttry {\n\t\t\tcursor = mDb.query(Const.TableSchema.TABLE_NAME, null, null, null, null, null, null);\n\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\tdo {\n\t\t\t\t\tString tableName = cursor.getString(cursor\n\t\t\t\t\t\t\t.getColumnIndexOrThrow(Const.TableSchema.COLUMN_NAME));\n\t\t\t\t\tint tableType = cursor.getInt(cursor\n\t\t\t\t\t\t\t.getColumnIndexOrThrow(Const.TableSchema.COLUMN_TYPE));\n\t\t\t\t\tif (shouldDropThisTable(tableName, tableType)) {\n\t\t\t\t\t\t// need to drop tableNameDB\n\t\t\t\t\t\tLitePalLog.d(TAG, \"need to drop \" + tableName);\n\t\t\t\t\t\tdropTableNames.add(tableName);\n\t\t\t\t\t}\n\t\t\t\t} while (cursor.moveToNext());\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t} finally {\n\t\t\tif (cursor != null) {\n\t\t\t\tcursor.close();\n\t\t\t}\n\t\t}\n\t\treturn dropTableNames;\n\t}\n\n\t/**\n\t * Get a list only with table names.\n\t * \n\t * @return A list only contains table names.\n\t */\n\tprivate List<String> pickTableNamesFromTableModels() {\n\t\tList<String> tableNames = new ArrayList<>();\n\t\tfor (TableModel tableModel : mTableModels) {\n\t\t\ttableNames.add(tableModel.getTableName());\n\t\t}\n\t\treturn tableNames;\n\t}\n\n\t/**\n\t * It gets all the table names generated by the mapping classes and create a\n\t * table name list. Compare table name list with the table name passed in.\n\t * If the table name is not existed in the table name list and the table\n\t * type is {@link org.litepal.util.Const.TableSchema#NORMAL_TABLE}, then this table should be\n\t * dropped.\n\t * \n\t * @param tableName\n\t *            The table name to check.\n\t * @param tableType\n\t *            The table type to check.\n\t * @return If this table should be dropped return true. Otherwise return\n\t *         false.\n\t */\n\tprivate boolean shouldDropThisTable(String tableName, int tableType) {\n\t\treturn !BaseUtility.containsIgnoreCases(pickTableNamesFromTableModels(), tableName)\n\t\t\t\t&& tableType == Const.TableSchema.NORMAL_TABLE;\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/Generator.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.litepal.LitePalBase;\nimport org.litepal.exceptions.DatabaseGenerateException;\nimport org.litepal.parser.LitePalAttr;\nimport org.litepal.tablemanager.model.AssociationsModel;\nimport org.litepal.tablemanager.model.TableModel;\nimport org.litepal.util.BaseUtility;\n\nimport android.database.SQLException;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.text.TextUtils;\n\n/**\n * This class is the basic class for managing database dynamically. It is used\n * to create or update tables by the mapping classes from litepal.xml file.\n * Generator is a superclass, it just read the fields from classes and format\n * the fields types into database types. The analysis job is delegated to the\n * subclasses to do. Then subclasses can invoke the execute method to finish the\n * job or they can override to do their own logic.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic abstract class Generator extends LitePalBase {\n\tpublic static final String TAG = \"Generator\";\n\n\t/**\n\t * The collection contains all table models. Use a global variable store\n\t * table model to improve performance. Avoiding look up for table model each\n\t * time.\n\t */\n\tprivate Collection<TableModel> mTableModels;\n\n\t/**\n\t * The collection contains all association models.\n\t */\n\tprivate Collection<AssociationsModel> mAllRelationModels;\n\n\t/**\n\t * This is a shortcut way to get all the table models for each model class\n\t * defined in the mapping list. No need to iterate all the model classes and\n\t * get table model for each one.\n\t * \n\t * @return A collection contains all table models.\n\t */\n\tprotected Collection<TableModel> getAllTableModels() {\n\t\tif (mTableModels == null) {\n\t\t\tmTableModels = new ArrayList<>();\n\t\t}\n\t\tif (!canUseCache()) {\n\t\t\tmTableModels.clear();\n\t\t\tfor (String className : LitePalAttr.getInstance().getClassNames()) {\n\t\t\t\tmTableModels.add(getTableModel(className));\n\t\t\t}\n\t\t}\n\t\treturn mTableModels;\n\t}\n\n\t/**\n\t * This method is used to get all the association models which in the\n\t * mapping list of litepal.xml file.\n\t * \n\t * @return Collection of RelationModel for all the mapping classes.\n\t */\n\tprotected Collection<AssociationsModel> getAllAssociations() {\n\t\tif (mAllRelationModels == null || mAllRelationModels.isEmpty()) {\n\t\t\tmAllRelationModels = getAssociations(LitePalAttr.getInstance().getClassNames());\n\t\t}\n\t\treturn mAllRelationModels;\n\t}\n\n\t/**\n\t * Use the parameter SQLiteDatabase to execute the passing SQLs. Subclasses\n\t * can add their own logic when do the executing job by overriding this\n\t * method.\n\t * \n\t * @param sqls\n\t *            SQLs that want to execute.\n\t * @param db\n\t *            instance of SQLiteDatabase\n\t */\n\tprotected void execute(List<String> sqls, SQLiteDatabase db) {\n\t\tString throwSQL = \"\";\n\t\ttry {\n\t\t\tif (sqls != null && !sqls.isEmpty()) {\n\t\t\t\tfor (String sql : sqls) {\n                    if (!TextUtils.isEmpty(sql)) {\n                        throwSQL = BaseUtility.changeCase(sql);\n                        db.execSQL(throwSQL);\n                    }\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new DatabaseGenerateException(DatabaseGenerateException.SQL_ERROR + throwSQL);\n\t\t}\n\t}\n\n\t/**\n\t * Add association to all the tables based on the associations between class\n\t * models.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @param force\n\t *            Drop the table first if it already exists.\n\t */\n\tprivate static void addAssociation(SQLiteDatabase db, boolean force) {\n\t\tAssociationCreator associationsCreator = new Creator();\n\t\tassociationsCreator.addOrUpdateAssociation(db, force);\n\t}\n\n\t/**\n\t * Update associations to all the associated tables in the database. Remove\n\t * dump foreign key columns and dump intermediate join tables.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t */\n\tprivate static void updateAssociations(SQLiteDatabase db) {\n\t\tAssociationUpdater associationUpgrader = new Upgrader();\n\t\tassociationUpgrader.addOrUpdateAssociation(db, false);\n\t}\n\n\t/**\n\t * Upgrade all the tables in the database, including remove dump columns and\n\t * add new columns.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t */\n\tprivate static void upgradeTables(SQLiteDatabase db) {\n\t\tUpgrader upgrader = new Upgrader();\n\t\tupgrader.createOrUpgradeTable(db, false);\n\t}\n\n\t/**\n\t * Create tables based on the class models defined in the litepal.xml file.\n\t * After the tables are created, add association to these tables based on\n\t * the associations between class models.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @param force\n\t *            Drop the table first if it already exists.\n\t */\n\tprivate static void create(SQLiteDatabase db, boolean force) {\n\t\tCreator creator = new Creator();\n\t\tcreator.createOrUpgradeTable(db, force);\n\t}\n\n\t/**\n\t * Drop the tables which are no longer exist in the mapping list but created\n\t * before.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t */\n\tprivate static void drop(SQLiteDatabase db) {\n\t\tDropper dropper = new Dropper();\n\t\tdropper.createOrUpgradeTable(db, false);\n\t}\n\n\t/**\n\t * If the table models in the collection has the same size as classes\n\t * defined in the mapping list, it means that the table models are exist.\n\t * Can use cache.\n\t * \n\t * @return Can use the cache for the table models or not.\n\t */\n\tprivate boolean canUseCache() {\n\t\tif (mTableModels == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn mTableModels.size() == LitePalAttr.getInstance().getClassNames().size();\n\t}\n\n\t/**\n\t * Create tables based on the class models defined in the litepal.xml file.\n\t * After the tables are created, add association to these tables based on\n\t * the associations between class models.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t */\n\tstatic void create(SQLiteDatabase db) {\n\t\tcreate(db, true);\n\t\taddAssociation(db, true);\n\t}\n\n\t/**\n\t * Upgrade tables to make sure when model classes are changed, the\n\t * corresponding tables in the database should be always synchronized with\n\t * them.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t */\n\tstatic void upgrade(SQLiteDatabase db) {\n\t\tdrop(db);\n\t\tcreate(db, false);\n\t\tupdateAssociations(db);\n\t\tupgradeTables(db);\n\t\taddAssociation(db, false);\n\t}\n\n\t/**\n\t * Analysis the TableModel by the purpose of subclasses, and generate a SQL\n\t * to do the intention job. The implementation of this method is totally\n\t * delegated to the subclasses.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @param force\n\t *            Drop the table first if it already exists.\n\t */\n\tprotected abstract void createOrUpgradeTable(SQLiteDatabase db, boolean force);\n\n\t/**\n\t * Analysis the {@link org.litepal.tablemanager.model.AssociationsModel} by the purpose of subclasses, and\n\t * generate a SQL to do the intention job. The implementation of this method\n\t * is totally delegated to the subclasses.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @param force\n\t *            Drop the table first if it already exists.\n\t */\n\tprotected abstract void addOrUpdateAssociation(SQLiteDatabase db, boolean force);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/LitePalOpenHelper.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager;\n\nimport android.content.Context;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteDatabase.CursorFactory;\nimport android.database.sqlite.SQLiteOpenHelper;\n\nimport org.litepal.LitePalApplication;\nimport org.litepal.Operator;\nimport org.litepal.parser.LitePalAttr;\nimport org.litepal.tablemanager.callback.DatabaseListener;\nimport org.litepal.util.SharedUtil;\n\n/**\n * The database helper to generate and manage the tables. It will automate\n * create or upgrade the database file depends on the parameters passed in.\n *\n * LitePal makes it easy for managing tables. It used the dynamic features of\n * Java with reflection API to achieve that. Developers won't need to write\n * their own SQL for managing tables, LitePal will do that for them. Developers\n * just need to write their model classes and add right associations. LitePal\n * will take all the rest job to manager tables in database.\n *\n * @author Tony Green\n * @since 1.0\n */\nclass LitePalOpenHelper extends SQLiteOpenHelper {\n    public static final String TAG = \"LitePalHelper\";\n\n    /**\n     * The standard constructor for SQLiteOpenHelper.\n     *\n     * @param context\n     *            To use to open or create the database.\n     * @param name\n     *            The database file.\n     * @param factory\n     *            To use for creating cursor objects, or null for the default\n     *            version number of the database (starting at 1); if the\n     *            database is older, onUpgrade.\n     * @param version\n     *            (SQLiteDatabase, int, int) will be used to upgrade the\n     *            database; if the database is newer,\n     *            onDowngrade(SQLiteDatabase, int, int) will be used to\n     *            downgrade the database\n     */\n    LitePalOpenHelper(Context context, String name, CursorFactory factory, int version) {\n        super(context, name, factory, version);\n    }\n\n    /**\n     * A simple constructor for SQLiteOpenHelper with null for CursorFactory as\n     * default.\n     *\n     * @param dbName\n     *            The database file.\n     * @param version\n     *            (SQLiteDatabase, int, int) will be used to upgrade the\n     *            database; if the database is newer,\n     *            onDowngrade(SQLiteDatabase, int, int) will be used to\n     *            downgrade the database\n     */\n    LitePalOpenHelper(String dbName, int version) {\n        this(LitePalApplication.getContext(), dbName, null, version);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        Generator.create(db);\n        final DatabaseListener listener = Operator.getDBListener();\n        if (listener != null) {\n            LitePalApplication.sHandler.post(new Runnable() {\n                @Override\n                public void run() {\n                    listener.onCreate();\n                }\n            });\n        }\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, final int oldVersion, final int newVersion) {\n        Generator.upgrade(db);\n        SharedUtil.updateVersion(LitePalAttr.getInstance().getExtraKeyName(), newVersion);\n        final DatabaseListener listener = Operator.getDBListener();\n        if (listener != null) {\n            LitePalApplication.sHandler.post(new Runnable() {\n                @Override\n                public void run() {\n                    listener.onUpgrade(oldVersion, newVersion);\n                }\n            });\n        }\n    }\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/Upgrader.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager;\n\nimport android.database.sqlite.SQLiteDatabase;\nimport android.text.TextUtils;\n\nimport org.litepal.crud.model.AssociationsInfo;\nimport org.litepal.tablemanager.model.ColumnModel;\nimport org.litepal.tablemanager.model.TableModel;\nimport org.litepal.util.Const;\nimport org.litepal.util.DBUtility;\nimport org.litepal.util.LitePalLog;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * Upgrade the database. The first step is to remove the columns that can not\n * find the corresponding field in the model class. Then add the new added field\n * as new column into the table. At last it will check all the types of columns\n * to see which are changed.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class Upgrader extends AssociationUpdater {\n\t/**\n\t * Model class for table.\n\t */\n\tprotected TableModel mTableModel;\n\n    /**\n     * Model class for table from database.\n     */\n    protected TableModel mTableModelDB;\n\n    /**\n     * Indicates that column constraints has changed or not.\n     */\n    private boolean hasConstraintChanged;\n\n\t/**\n\t * Analyzing the table model, them remove the dump columns and add new\n\t * columns of a table.\n\t */\n\t@Override\n\tprotected void createOrUpgradeTable(SQLiteDatabase db, boolean force) {\n\t\tmDb = db;\n\t\tfor (TableModel tableModel : getAllTableModels()) {\n\t\t\tmTableModel = tableModel;\n            mTableModelDB = getTableModelFromDB(tableModel.getTableName());\n            LitePalLog.d(TAG, \"createOrUpgradeTable: model is \" + mTableModel.getTableName());\n            upgradeTable();\n\t\t}\n\t}\n\n\t/**\n\t * Upgrade table actions. Include remove dump columns, add new columns and\n\t * change column types. All the actions above will be done by the description\n     * order.\n\t */\n\tprivate void upgradeTable() {\n        if (hasNewUniqueOrNotNullColumn()) {\n            // Need to drop the table and create new one. Cause unique column can not be added, and null data can not be migrated.\n            createOrUpgradeTable(mTableModel, mDb, true);\n            // add foreign keys of the table.\n            Collection<AssociationsInfo> associationsInfo = getAssociationInfo(mTableModel.getClassName());\n            for (AssociationsInfo info : associationsInfo) {\n                if (info.getAssociationType() == Const.Model.MANY_TO_ONE\n                        || info.getAssociationType() == Const.Model.ONE_TO_ONE) {\n                    if (info.getClassHoldsForeignKey().equalsIgnoreCase(mTableModel.getClassName())) {\n                        String associatedTableName = DBUtility.getTableNameByClassName(info.getAssociatedClassName());\n                        addForeignKeyColumn(mTableModel.getTableName(), associatedTableName, mTableModel.getTableName(), mDb);\n                    }\n                }\n            }\n        } else {\n            hasConstraintChanged = false;\n            removeColumns(findColumnsToRemove());\n            addColumns(findColumnsToAdd());\n            changeColumnsType(findColumnTypesToChange());\n            changeColumnsConstraints();\n        }\n\t}\n\n    /**\n     * Check if the current model add or upgrade an unique or not null column.\n     * @return True if has new unique or not null column. False otherwise.\n     */\n    private boolean hasNewUniqueOrNotNullColumn() {\n        Collection<ColumnModel> columnModels = mTableModel.getColumnModels();\n        for (ColumnModel columnModel : columnModels) {\n            if (columnModel.isIdColumn()) continue; // id don't check unique or nullable, we never upgrade it.\n            ColumnModel columnModelDB = mTableModelDB.getColumnModelByName(columnModel.getColumnName());\n            if (columnModel.isUnique()) {\n                if (columnModelDB == null || !columnModelDB.isUnique()) {\n                    return true;\n                }\n            }\n            if (columnModelDB != null && !columnModel.isNullable() && columnModelDB.isNullable()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n\t/**\n\t * It will find the difference between class model and table model. If\n\t * there's a field in the class without a corresponding column in the table,\n\t * this field is a new added column. This method find all new added columns.\n\t * \n\t * @return List with ColumnModel contains information of new columns.\n\t */\n\tprivate List<ColumnModel> findColumnsToAdd() {\n        List<ColumnModel> columnsToAdd = new ArrayList<>();\n        for (ColumnModel columnModel : mTableModel.getColumnModels()) {\n            String columnName = columnModel.getColumnName();\n            if (!mTableModelDB.containsColumn(columnName)) {\n                // add column action\n                columnsToAdd.add(columnModel);\n            }\n        }\n\t\treturn columnsToAdd;\n\t}\n\n\t/**\n\t * This method helps find the difference between table model from class and\n\t * table model from database. Database should always be synchronized with\n\t * model class. If there're some fields are removed from class, the table\n\t * model from database will be compared to find out which fields are\n\t * removed. But there're still some exceptions. The columns named id or _id\n\t * won't ever be removed. The foreign key column will be checked some where\n\t * else, not from here.\n\t * \n\t * @return A list with column names need to remove.\n\t */\n\tprivate List<String> findColumnsToRemove() {\n        String tableName = mTableModel.getTableName();\n\t\tList<String> removeColumns = new ArrayList<>();\n        Collection<ColumnModel> columnModels = mTableModelDB.getColumnModels();\n        for (ColumnModel columnModel : columnModels) {\n            String dbColumnName = columnModel.getColumnName();\n            if (isNeedToRemove(dbColumnName)) {\n                removeColumns.add(dbColumnName);\n            }\n        }\n        LitePalLog.d(TAG, \"remove columns from \" + tableName + \" >> \" + removeColumns);\n\t\treturn removeColumns;\n\t}\n\n\t/**\n\t * It will check each class in the mapping list. Find their types for each\n\t * field is changed or not by comparing with the types in table columns. If\n\t * there's a column have same name as a field in class but with different\n\t * type, then it's a type changed column.\n\t *\n\t * @return A list contains all ColumnModel which type are changed from database.\n\t */\n\tprivate List<ColumnModel> findColumnTypesToChange() {\n        List<ColumnModel> columnsToChangeType = new ArrayList<>();\n        for (ColumnModel columnModelDB : mTableModelDB.getColumnModels()) {\n            for (ColumnModel columnModel : mTableModel.getColumnModels()) {\n                if (columnModelDB.getColumnName().equalsIgnoreCase(columnModel.getColumnName())) {\n                    if (!columnModelDB.getColumnType().equalsIgnoreCase(columnModel.getColumnType())) {\n                        if (columnModel.getColumnType().equalsIgnoreCase(\"blob\") && TextUtils.isEmpty(columnModelDB.getColumnType())) {\n                            // Case for binary array type upgrade. Do nothing under this condition.\n                        } else {\n                            // column type is changed\n                            columnsToChangeType.add(columnModel);\n                        }\n                    }\n                    if (!hasConstraintChanged) {\n                        // for reducing loops, check column constraints change here.\n                        LitePalLog.d(TAG, \"default value db is:\" + columnModelDB.getDefaultValue() + \", default value is:\" + columnModel.getDefaultValue());\n                        if (columnModelDB.isNullable() != columnModel.isNullable() ||\n                                !columnModelDB.getDefaultValue().equalsIgnoreCase(columnModel.getDefaultValue()) ||\n                                columnModelDB.hasIndex() != columnModel.hasIndex() ||\n                                (columnModelDB.isUnique() && !columnModel.isUnique())) { // unique constraint can not be added\n                            hasConstraintChanged = true;\n                        }\n                    }\n                }\n            }\n        }\n\t\treturn columnsToChangeType;\n\t}\n\n\t/**\n\t * Tell LitePal the column is need to remove or not. The column can be\n\t * remove only on the condition that the following three rules are all\n\t * passed. First the corresponding field for this column is removed in the\n\t * class. Second this column is not an id column. Third this column is not a\n\t * foreign key column.\n\t * \n\t * @param columnName\n\t *            The column name to judge\n\t * @return Need to remove return true, otherwise return false.\n\t */\n\tprivate boolean isNeedToRemove(String columnName) {\n\t\treturn isRemovedFromClass(columnName) && !isIdColumn(columnName)\n\t\t\t\t&& !isForeignKeyColumn(mTableModel, columnName);\n\t}\n\n\t/**\n\t * Read a column name from database, and judge the corresponding field in\n\t * class is removed or not.\n\t * \n\t * @param columnName\n\t *            The column name to judge.\n\t * @return If it's removed return true, or return false.\n\t */\n\tprivate boolean isRemovedFromClass(String columnName) {\n        return !mTableModel.containsColumn(columnName);\n\t}\n\n\t/**\n\t * Generate a SQL for add new column into the existing table.\n\t * \n\t * @param columnModel\n\t *            Which contains column info.\n\t * @return A SQL to add new column.\n\t */\n\tprivate List<String> generateAddColumnSQLs(ColumnModel columnModel) {\n\t    List<String> sqls = new ArrayList<>();\n\t    sqls.add(generateAddColumnSQL(mTableModel.getTableName(), columnModel));\n\t    if (columnModel.hasIndex()) {\n\t        sqls.add(generateCreateIndexSQL(mTableModel.getTableName(), columnModel));\n        }\n\t\treturn sqls;\n\t}\n\n\t/**\n\t * This method create a SQL array for the all new columns to add them into\n\t * table.\n\t * \n\t * @param columnModelList\n\t *            List with ColumnModel to add new column.\n\t * @return A SQL list contains add all new columns job.\n\t */\n\tprivate List<String> getAddColumnSQLs(List<ColumnModel> columnModelList) {\n\t\tList<String> sqls = new ArrayList<>();\n\t\tfor (ColumnModel columnModel : columnModelList) {\n\t\t\tsqls.addAll(generateAddColumnSQLs(columnModel));\n\t\t}\n\t\treturn sqls;\n\t}\n\n    /**\n     * When some fields are removed from class, the table should synchronize the\n     * changes by removing the corresponding columns.\n     *\n     * @param removeColumnNames\n     *            The column names that need to remove.\n     */\n    private void removeColumns(List<String> removeColumnNames) {\n        LitePalLog.d(TAG, \"do removeColumns \" + removeColumnNames);\n        removeColumns(removeColumnNames, mTableModel.getTableName());\n        for (String columnName : removeColumnNames) {\n            mTableModelDB.removeColumnModelByName(columnName);\n        }\n    }\n\n\t/**\n\t * When some fields are added into the class after last upgrade, the table\n\t * should synchronize the changes by adding the corresponding columns.\n\t * \n\t * @param columnModelList\n\t *            List with ColumnModel to add new column.\n\t */\n\tprivate void addColumns(List<ColumnModel> columnModelList) {\n        LitePalLog.d(TAG, \"do addColumn\");\n\t\texecute(getAddColumnSQLs(columnModelList), mDb);\n        for (ColumnModel columnModel : columnModelList) {\n            mTableModelDB.addColumnModel(columnModel);\n        }\n\t}\n\n\t/**\n\t * When some fields type are changed in class, the table should drop the\n\t * before columns and create new columns with same name but new types.\n\t * \n\t * @param columnModelList\n\t *            List with ColumnModel to change column type.\n\t */\n\tprivate void changeColumnsType(List<ColumnModel> columnModelList) {\n        LitePalLog.d(TAG, \"do changeColumnsType\");\n        List<String> columnNames = new ArrayList<>();\n        if (columnModelList != null && !columnModelList.isEmpty()) {\n            for (ColumnModel columnModel : columnModelList) {\n                columnNames.add(columnModel.getColumnName());\n            }\n        }\n\t\tremoveColumns(columnNames);\n\t\taddColumns(columnModelList);\n\t}\n\n    /**\n     * When fields annotation changed in class, table should change the corresponding constraints\n     * make them sync to the fields annotation.\n     */\n    private void changeColumnsConstraints() {\n        if (hasConstraintChanged) {\n            LitePalLog.d(TAG, \"do changeColumnsConstraints\");\n            execute(getChangeColumnsConstraintsSQL(), mDb);\n        }\n    }\n\n    /**\n     * This method create a SQL array for the whole changing column constraints job.\n     * @return A SQL list contains create temporary table, create new table, add foreign keys,\n     *         migrate data and drop temporary table.\n     */\n    private List<String> getChangeColumnsConstraintsSQL() {\n        String alterToTempTableSQL = generateAlterToTempTableSQL(mTableModel.getTableName());\n        String createNewTableSQL = generateCreateTableSQL(mTableModel);\n        List<String> addForeignKeySQLs = generateAddForeignKeySQL();\n        String dataMigrationSQL = generateDataMigrationSQL(mTableModelDB);\n        String dropTempTableSQL = generateDropTempTableSQL(mTableModel.getTableName());\n        List<String> createIndexSQLs = generateCreateIndexSQLs(mTableModel);\n        List<String> sqls = new ArrayList<>();\n        sqls.add(alterToTempTableSQL);\n        sqls.add(createNewTableSQL);\n        sqls.addAll(addForeignKeySQLs);\n        sqls.add(dataMigrationSQL);\n        sqls.add(dropTempTableSQL);\n        sqls.addAll(createIndexSQLs);\n        LitePalLog.d(TAG, \"generateChangeConstraintSQL >> \");\n        for (String sql : sqls) {\n            LitePalLog.d(TAG, sql);\n        }\n        LitePalLog.d(TAG, \"<< generateChangeConstraintSQL\");\n        return sqls;\n    }\n\n    /**\n     * Generate a SQL List for adding foreign keys. Changing constraints job should remain all the\n     * existing columns including foreign keys. This method add origin foreign keys after creating\n     * table.\n     * @return A SQL List for adding foreign keys.\n     */\n    private List<String> generateAddForeignKeySQL() {\n        List<String> addForeignKeySQLs = new ArrayList<>();\n        List<String> foreignKeyColumns = getForeignKeyColumns(mTableModel);\n        for (String foreignKeyColumn : foreignKeyColumns) {\n            if (!mTableModel.containsColumn(foreignKeyColumn)) {\n                ColumnModel columnModel = new ColumnModel();\n                columnModel.setColumnName(foreignKeyColumn);\n                columnModel.setColumnType(\"integer\");\n                addForeignKeySQLs.add(generateAddColumnSQL(mTableModel.getTableName(), columnModel));\n            }\n        }\n        return addForeignKeySQLs;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/callback/DatabaseListener.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.callback;\n\n/**\n * Callback for listening database create and upgrade events.\n * @author Tony Green\n * @since 2.0\n */\npublic interface DatabaseListener {\n\n    void onCreate();\n\n    void onUpgrade(int oldVersion, int newVersion);\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/model/AssociationsModel.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.model;\n\nimport org.litepal.util.Const;\n\n/**\n * This is a model class for table associations. It stores table name,\n * associated table name, table name which holds foreign key, and association\n * type. Relations have three types. One2One, Many2One and Many2Many. If the\n * association type is One2One or Many2One, the foreign key will be on the side\n * of tableHoldsForeignKey. If the association is Many2Many, a intermediate join\n * table will be built and named by the concatenation of the two target table\n * names in alphabetical order with underline in the middle.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class AssociationsModel {\n\n\t/**\n\t * Table name.\n\t */\n\tprivate String tableName;\n\n\t/**\n\t * Associated table name.\n\t */\n\tprivate String associatedTableName;\n\n\t/**\n\t * The table which holds foreign key.\n\t */\n\tprivate String tableHoldsForeignKey;\n\n\t/**\n\t * The association type, including {@link Const.Model#MANY_TO_ONE},\n\t * {@link Const.Model#MANY_TO_MANY}, {@link Const.Model#ONE_TO_ONE}.\n\t */\n\tprivate int associationType;\n\n\t/**\n\t * Get table name.\n\t * \n\t * @return Return the table name.\n\t */\n\tpublic String getTableName() {\n\t\treturn tableName;\n\t}\n\n\t/**\n\t * Set table name.\n\t * \n\t * @param tableName\n\t *            The table name to set.\n\t */\n\tpublic void setTableName(String tableName) {\n\t\tthis.tableName = tableName;\n\t}\n\n\t/**\n\t * Get associated table name.\n\t * \n\t * @return Return the associated table name.\n\t */\n\tpublic String getAssociatedTableName() {\n\t\treturn associatedTableName;\n\t}\n\n\t/**\n\t * Set associated table name.\n\t * \n\t * @param associatedTableName\n\t *            The associated table name.\n\t */\n\tpublic void setAssociatedTableName(String associatedTableName) {\n\t\tthis.associatedTableName = associatedTableName;\n\t}\n\n\t/**\n\t * Get the table which holds foreign key.\n\t * \n\t * @return The table which holds foreign key.\n\t */\n\tpublic String getTableHoldsForeignKey() {\n\t\treturn tableHoldsForeignKey;\n\t}\n\n\t/**\n\t * Set the table which holds foreign key.\n\t * \n\t * @param tableHoldsForeignKey\n\t *            The table which holds foreign key to set.\n\t */\n\tpublic void setTableHoldsForeignKey(String tableHoldsForeignKey) {\n\t\tthis.tableHoldsForeignKey = tableHoldsForeignKey;\n\t}\n\n\t/**\n\t * Get the association type.\n\t * \n\t * @return The association type.\n\t */\n\tpublic int getAssociationType() {\n\t\treturn associationType;\n\t}\n\n\t/**\n\t * Set the association type.\n\t * \n\t * @param associationType\n\t *            Within {@link Const.Model#MANY_TO_ONE},\n\t *            {@link Const.Model#MANY_TO_MANY},\n\t *            {@link Const.Model#ONE_TO_ONE}.\n\t */\n\tpublic void setAssociationType(int associationType) {\n\t\tthis.associationType = associationType;\n\t}\n\n\t/**\n\t * Override equals method to make sure that if two associated tables in the\n\t * association model are same ignoring sides, they are same association\n\t * model.\n\t */\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o instanceof AssociationsModel) {\n\t\t\tAssociationsModel association = (AssociationsModel) o;\n\t\t\tif (association.getTableName() != null && association.getAssociatedTableName() != null) {\n\t\t\t\tif (association.getAssociationType() == associationType\n\t\t\t\t\t\t&& association.getTableHoldsForeignKey().equals(tableHoldsForeignKey)) {\n\t\t\t\t\tif (association.getTableName().equals(tableName)\n\t\t\t\t\t\t\t&& association.getAssociatedTableName().equals(associatedTableName)\n\t\t\t\t\t\t\t&& association.getTableHoldsForeignKey().equals(tableHoldsForeignKey)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} else if (association.getTableName().equals(associatedTableName)\n\t\t\t\t\t\t\t&& association.getAssociatedTableName().equals(tableName)\n\t\t\t\t\t\t\t&& association.getTableHoldsForeignKey().equals(tableHoldsForeignKey)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/model/ColumnModel.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.model;\n\nimport android.text.TextUtils;\n\n/**\n * This is a model class for columns. It stores column name, column type, and column constraints\n * information.\n * @author Tony Green\n * @since 1.3\n */\npublic class ColumnModel {\n\n    /**\n     * Name of column.\n     */\n    private String columnName;\n\n    /**\n     * Type for column.\n     */\n    private String columnType;\n\n    /**\n     * Nullable constraint.\n     */\n    private boolean isNullable = true;\n\n    /**\n     * Unique constraint.\n     */\n    private boolean isUnique = false;\n\n    /**\n     * Default constraint.\n     */\n    private String defaultValue = \"\";\n\n    /**\n     * Has index for this column or not.\n     */\n    private boolean hasIndex = false;\n\n    public String getColumnName() {\n        return columnName;\n    }\n\n    public void setColumnName(String columnName) {\n        this.columnName = columnName;\n    }\n\n    public String getColumnType() {\n        return columnType;\n    }\n\n    public void setColumnType(String columnType) {\n        this.columnType = columnType;\n    }\n\n    public boolean isNullable() {\n        return isNullable;\n    }\n\n    public void setNullable(boolean isNullable) {\n        this.isNullable = isNullable;\n    }\n\n    public boolean isUnique() {\n        return isUnique;\n    }\n\n    public void setUnique(boolean isUnique) {\n        this.isUnique = isUnique;\n    }\n\n    public String getDefaultValue() {\n        return defaultValue;\n    }\n\n    public boolean hasIndex() {\n        return hasIndex;\n    }\n\n    public void setHasIndex(boolean hasIndex) {\n        this.hasIndex = hasIndex;\n    }\n\n    public void setDefaultValue(String defaultValue) {\n        if (\"text\".equalsIgnoreCase(columnType)) {\n            if (!TextUtils.isEmpty(defaultValue)) {\n                this.defaultValue = \"'\" + defaultValue + \"'\";\n            }\n        } else {\n            this.defaultValue = defaultValue;\n        }\n    }\n\n    /**\n     * Judge current ColumnModel is id column or not.\n     * @return True if it's id column. False otherwise.\n     */\n    public boolean isIdColumn() {\n        return \"_id\".equalsIgnoreCase(columnName) || \"id\".equalsIgnoreCase(columnName);\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/model/GenericModel.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.model;\n\n/**\n * This is a model class for generic table. It stores table name, value column name, value column\n * type and value id column name. This class is used to create generic tables when generic collection\n * fields are declared in the model class.\n *\n * @author Tony Green\n * @since 1.4\n */\npublic class GenericModel {\n\n    /**\n     * Table name.\n     */\n    private String tableName;\n\n    /**\n     * Column name for storing value.\n     */\n    private String valueColumnName;\n\n    /**\n     * Column type for storing value.\n     */\n    private String valueColumnType;\n\n    /**\n     * Column name for reference with main table.\n     */\n    private String valueIdColumnName;\n\n    /**\n     * Only used when query generic data. This is cache fields for improving performance.\n     */\n    private String getMethodName;\n\n    public String getTableName() {\n        return tableName;\n    }\n\n    public void setTableName(String tableName) {\n        this.tableName = tableName;\n    }\n\n    public String getValueColumnName() {\n        return valueColumnName;\n    }\n\n    public void setValueColumnName(String valueColumnName) {\n        this.valueColumnName = valueColumnName;\n    }\n\n    public String getValueColumnType() {\n        return valueColumnType;\n    }\n\n    public void setValueColumnType(String valueColumnType) {\n        this.valueColumnType = valueColumnType;\n    }\n\n    public String getValueIdColumnName() {\n        return valueIdColumnName;\n    }\n\n    public void setValueIdColumnName(String valueIdColumnName) {\n        this.valueIdColumnName = valueIdColumnName;\n    }\n\n    public String getGetMethodName() {\n        return getMethodName;\n    }\n\n    public void setGetMethodName(String getMethodName) {\n        this.getMethodName = getMethodName;\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/model/TableModel.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.model;\n\nimport org.litepal.util.BaseUtility;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * This is a model class for tables. It stores a table name and a HashMap for\n * columns in the table.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class TableModel {\n\n\t/**\n\t * Table name.\n\t */\n\tprivate String tableName;\n\n    /**\n     * A map contains all column models with column name, type and constraints.\n     */\n    private Map<String, ColumnModel> columnModelMap = new HashMap<>();\n\n\t/**\n\t * Class name for the table name. This value might be null. Don't rely on it.\n\t */\n\tprivate String className;\n\n\t/**\n\t * Get table name.\n\t * \n\t * @return Name of table.\n\t */\n\tpublic String getTableName() {\n\t\treturn tableName;\n\t}\n\n\t/**\n\t * Set table name.\n\t * \n\t * @param tableName\n\t *            Name of table.\n\t */\n\tpublic void setTableName(String tableName) {\n\t\tthis.tableName = tableName;\n\t}\n\n\t/**\n\t * Get class name.\n\t * \n\t * @return Return the class name or null.\n\t */\n\tpublic String getClassName() {\n\t\treturn className;\n\t}\n\n\t/**\n\t * Set class name.\n\t * \n\t * @param className\n\t *            The class name.\n\t */\n\tpublic void setClassName(String className) {\n\t\tthis.className = className;\n\t}\n\n    /**\n     * Add a column model into the table model.\n     *\n     * @param columnModel\n     *            A column model contains name, type and constraints.\n     */\n    public void addColumnModel(ColumnModel columnModel) {\n        columnModelMap.put(BaseUtility.changeCase(columnModel.getColumnName()), columnModel);\n    }\n\n    /**\n     * Find all the column models of the current table model.\n     * @return A list contains all column models.\n     */\n    public Collection<ColumnModel> getColumnModels() {\n        return columnModelMap.values();\n    }\n\n    /**\n     * Find the ColumnModel which can map the column name passed in.\n     * @param columnName\n     *          Name of column.\n     * @return A ColumnModel which can map the column name passed in. Or null.\n     */\n    public ColumnModel getColumnModelByName(String columnName) {\n        return columnModelMap.get(BaseUtility.changeCase(columnName));\n    }\n\n    /**\n     * Remove a column model by the specified column name.\n     * @param columnName\n     *          Name of the column to remove.\n     */\n    public void removeColumnModelByName(String columnName) {\n        columnModelMap.remove(BaseUtility.changeCase(columnName));\n    }\n\n    /**\n     * Judge the table model has such a column or not.\n     * @param columnName\n     *          The name of column to check.\n     * @return True if matches a column in the table model. False otherwise.\n     */\n    public boolean containsColumn(String columnName) {\n        return columnModelMap.containsKey(BaseUtility.changeCase(columnName));\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/typechange/BlobOrm.java",
    "content": "package org.litepal.tablemanager.typechange;\n\n/**\n * This class deals with byte type.\n *\n * @author Tony Green\n * @since 1.3.1\n */\npublic class BlobOrm extends OrmChange{\n\n    /**\n     * If the field type passed in is byte, it will change it into blob as\n     * column type.\n     */\n    @Override\n    public String object2Relation(String fieldType) {\n        if (fieldType != null) {\n            if (fieldType.equals(\"[B\")) {\n                return \"blob\";\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/typechange/BooleanOrm.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.typechange;\n\n/**\n * This class deals with boolean type.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class BooleanOrm extends OrmChange {\n\n\t/**\n\t * If the field type passed in is boolean, it will change it into integer as\n\t * column type.\n\t */\n\t@Override\n\tpublic String object2Relation(String fieldType) {\n\t\tif (fieldType != null) {\n\t\t\tif (fieldType.equals(\"boolean\") || fieldType.equals(\"java.lang.Boolean\")) {\n\t\t\t\treturn \"integer\";\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/typechange/DateOrm.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.typechange;\n\n/**\n * This class deals with date type.\n * \n * @author Tony Green\n * @since 1.1\n */\npublic class DateOrm extends OrmChange {\n\n\t/**\n\t * If the field type passed in is Date, it will change it into integer as\n\t * column type.\n\t */\n\t@Override\n\tpublic String object2Relation(String fieldType) {\n\t\tif (fieldType != null) {\n\t\t\tif (fieldType.equals(\"java.util.Date\")) {\n\t\t\t\treturn \"integer\";\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/typechange/DecimalOrm.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.typechange;\n\n/**\n * This class deals with decimal type.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class DecimalOrm extends OrmChange {\n\n\t/**\n\t * If the field type passed in is float or double, it will change it into\n\t * real as column type.\n\t */\n\t@Override\n\tpublic String object2Relation(String fieldType) {\n\t\tif (fieldType != null) {\n\t\t\tif (fieldType.equals(\"float\") || fieldType.equals(\"java.lang.Float\")) {\n\t\t\t\treturn \"real\";\n\t\t\t}\n\t\t\tif (fieldType.equals(\"double\") || fieldType.equals(\"java.lang.Double\")) {\n\t\t\t\treturn \"real\";\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/typechange/NumericOrm.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.typechange;\n\n/**\n * This class deals with numeric type.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class NumericOrm extends OrmChange {\n\n\t/**\n\t * If the field type passed in is int, long or short, it will change it into\n\t * integer as column type.\n\t */\n\t@Override\n\tpublic String object2Relation(String fieldType) {\n\t\tif (fieldType != null) {\n\t\t\tif (fieldType.equals(\"int\") || fieldType.equals(\"java.lang.Integer\")) {\n\t\t\t\treturn \"integer\";\n\t\t\t}\n\t\t\tif (fieldType.equals(\"long\") || fieldType.equals(\"java.lang.Long\")) {\n\t\t\t\treturn \"integer\";\n\t\t\t}\n\t\t\tif (fieldType.equals(\"short\") || fieldType.equals(\"java.lang.Short\")) {\n\t\t\t\treturn \"integer\";\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/typechange/OrmChange.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.typechange;\n\n/**\n * This is abstract super class to map the object field types to database column\n * types. The purpose of this class is to define a abstract method, and let all\n * subclasses implement it. Each subclass deals with a kind of changing and each\n * subclass will do their own logic to finish the changing job.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic abstract class OrmChange {\n\n\t/**\n\t * Subclasses implement this method to do their own logic to change types.\n\t *\n\t * @param fieldType\n\t *            The field type passed in.\n\t * @return Column type.\n\t */\n\tpublic abstract String object2Relation(String fieldType);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/tablemanager/typechange/TextOrm.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.tablemanager.typechange;\n\n/**\n * This class deals with text type.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class TextOrm extends OrmChange {\n\n\t/**\n\t * If the field type passed in is char or String, it will change it into\n\t * text as column type.\n\t */\n\t@Override\n\tpublic String object2Relation(String fieldType) {\n\t\tif (fieldType != null) {\n\t\t\tif (fieldType.equals(\"char\") || fieldType.equals(\"java.lang.Character\")) {\n\t\t\t\treturn \"text\";\n\t\t\t}\n\t\t\tif (fieldType.equals(\"java.lang.String\")) {\n\t\t\t\treturn \"text\";\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/util/BaseUtility.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.util;\n\nimport android.content.res.AssetManager;\nimport android.text.TextUtils;\n\nimport org.litepal.LitePalApplication;\nimport org.litepal.exceptions.LitePalSupportException;\nimport org.litepal.parser.LitePalAttr;\n\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.Locale;\n\n/**\n * A utility class to help LitePal with some base actions that might through any\n * components. These actions can help classes just do the jobs they care, and\n * help them out of the trivial work.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class BaseUtility {\n\n\t/**\n\t * Disable to create an instance of BaseUtility.\n\t */\n\tprivate BaseUtility() {\n\t}\n\n\t/**\n\t * It will change the case of the passing parameter into the case defined in\n\t * litepal.xml file.\n\t * \n\t * @param string\n\t *            The string want to change case.\n\t * @return The string after changing case. If the name is null, then simply\n\t *         return null.\n\t */\n\tpublic static String changeCase(String string) {\n\t\tif (string != null) {\n\t\t\tLitePalAttr litePalAttr = LitePalAttr.getInstance();\n\t\t\tString cases = litePalAttr.getCases();\n\t\t\tif (Const.Config.CASES_KEEP.equals(cases)) {\n\t\t\t\treturn string;\n\t\t\t} else if (Const.Config.CASES_UPPER.equals(cases)) {\n\t\t\t\treturn string.toUpperCase(Locale.US);\n\t\t\t}\n\t\t\treturn string.toLowerCase(Locale.US);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * This helper method makes up the shortage of contains method in Collection\n\t * to support the function of case insensitive contains. It only supports\n\t * the String generic type of collection, cause other types have no cases\n\t * concept.\n\t * \n\t * @param collection\n\t *            The collection contains string data.\n\t * @param string\n\t *            The string want to look for in the collection.\n\t * @return If the string is in the collection without case concern return\n\t *         true, otherwise return false. If the collection is null, return\n\t *         false.\n\t */\n\tpublic static boolean containsIgnoreCases(Collection<String> collection, String string) {\n\t\tif (collection == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (string == null) {\n\t\t\treturn collection.contains(null);\n\t\t}\n\t\tboolean contains = false;\n\t\tfor (String element : collection) {\n\t\t\tif (string.equalsIgnoreCase(element)) {\n\t\t\t\tcontains = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn contains;\n\t}\n\n\t/**\n\t * Capitalize make the first letter of the word be upper case.\n\t * \n\t * @param string\n\t *            The word to capitalize.\n\t * @return The word after capitalize.\n\t */\n\tpublic static String capitalize(String string) {\n\t\tif (!TextUtils.isEmpty(string)) {\n\t\t\treturn string.substring(0, 1).toUpperCase(Locale.US) + string.substring(1);\n\t\t}\n\t\treturn string == null ? null : \"\";\n\t}\n\n\t/**\n\t * Count how many marks existed in string.\n\t * \n\t * @param string\n\t *            The source sentence.\n\t * @param mark\n\t *            The specific substring to count.\n\t * @return The number of marks existed in string.\n\t */\n\tpublic static int count(String string, String mark) {\n\t\tif (!TextUtils.isEmpty(string) && !TextUtils.isEmpty(mark)) {\n\t\t\tint count = 0;\n\t\t\tint index = string.indexOf(mark);\n\t\t\twhile (index != -1) {\n\t\t\t\tcount++;\n\t\t\t\tstring = string.substring(index + mark.length());\n\t\t\t\tindex = string.indexOf(mark);\n\t\t\t}\n\t\t\treturn count;\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Check the number of question mark existed in conditions[0] equals the\n\t * number of rest conditions elements or not. If not equals, throws\n\t * LitePalSupportException.\n\t * \n\t * @param conditions\n\t *            A string array representing the WHERE part of an SQL\n\t *            statement.\n\t * @throws LitePalSupportException\n\t */\n\tpublic static void checkConditionsCorrect(String... conditions) {\n\t\tif (conditions != null) {\n\t\t\tint conditionsSize = conditions.length;\n\t\t\tif (conditionsSize > 0) {\n\t\t\t\tString whereClause = conditions[0];\n\t\t\t\tint placeHolderSize = BaseUtility.count(whereClause, \"?\");\n\t\t\t\tif (conditionsSize != placeHolderSize + 1) {\n\t\t\t\t\tthrow new LitePalSupportException(LitePalSupportException.UPDATE_CONDITIONS_EXCEPTION);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Judge a field type is supported or not. Currently only basic data types\n\t * and String are supported.\n\t * \n\t * @param fieldType\n\t *           Type of the field.\n\t * @return Supported return true, not supported return false.\n\t */\n\tpublic static boolean isFieldTypeSupported(String fieldType) {\n\t\tif (\"boolean\".equals(fieldType) || \"java.lang.Boolean\".equals(fieldType)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (\"float\".equals(fieldType) || \"java.lang.Float\".equals(fieldType)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (\"double\".equals(fieldType) || \"java.lang.Double\".equals(fieldType)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (\"int\".equals(fieldType) || \"java.lang.Integer\".equals(fieldType)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (\"long\".equals(fieldType) || \"java.lang.Long\".equals(fieldType)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (\"short\".equals(fieldType) || \"java.lang.Short\".equals(fieldType)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (\"char\".equals(fieldType) || \"java.lang.Character\".equals(fieldType)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (\"java.lang.String\".equals(fieldType) || \"java.util.Date\".equals(fieldType)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n    /**\n     * Judge a generic type is supported or not. Currently only basic data types\n     * and String are supported.\n     *\n     * @param genericType\n     *            Type of the generic field.\n     * @return Supported return true, not supported return false.\n     */\n    public static boolean isGenericTypeSupported(String genericType) {\n        if (\"java.lang.String\".equals(genericType)) {\n            return true;\n        } else if (\"java.lang.Integer\".equals(genericType)) {\n            return true;\n        } else if (\"java.lang.Float\".equals(genericType)) {\n            return true;\n        } else if (\"java.lang.Double\".equals(genericType)) {\n            return true;\n        } else if (\"java.lang.Long\".equals(genericType)) {\n            return true;\n        } else if (\"java.lang.Short\".equals(genericType)) {\n            return true;\n        } else if (\"java.lang.Boolean\".equals(genericType)) {\n            return true;\n        } else if (\"java.lang.Character\".equals(genericType)) {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * If the litepal.xml configuration file exists.\n     * @return True if exists, false otherwise.\n     */\n    public static boolean isLitePalXMLExists() {\n        try {\n            AssetManager assetManager = LitePalApplication.getContext().getAssets();\n            String[] fileNames = assetManager.list(\"\");\n            if (fileNames != null && fileNames.length > 0) {\n                for (String fileName : fileNames) {\n                    if (Const.Config.CONFIGURATION_FILE_NAME.equalsIgnoreCase(fileName)) {\n                        return true;\n                    }\n                }\n            }\n        } catch (IOException e) {\n        }\n        return false;\n    }\n\n    /**\n     * Check the existence of the specific class and method.\n     *\n     * @param className\n     * \t\t\tClass name with full package name.\n     * @param methodName\n     * \t\t\tMethod name.\n     * @return Return true if both of class and method are exist. Otherwise return false.\n     */\n    public static boolean isClassAndMethodExist(String className, String methodName) {\n        try {\n            Class<?> clazz = Class.forName(className);\n            Method[] methods = clazz.getMethods();\n            for (Method method : methods) {\n                if (methodName.equals(method.getName())) {\n                    return true;\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/util/Const.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.util;\n\npublic interface Const {\n\n\tpublic interface Model {\n\t\t/**\n\t\t * One2One constant value.\n\t\t */\n\t\tpublic static final int ONE_TO_ONE = 1;\n\n\t\t/**\n\t\t * Many2One constant value.\n\t\t */\n\t\tpublic static final int MANY_TO_ONE = 2;\n\n\t\t/**\n\t\t * Many2Many constant value.\n\t\t */\n\t\tpublic static final int MANY_TO_MANY = 3;\n\t}\n\n\tpublic interface Config {\n\t\t/**\n\t\t * The suffix for each database file.\n\t\t */\n\t\tpublic static final String DB_NAME_SUFFIX = \".db\";\n\n\t\t/**\n\t\t * Constant for upper case.\n\t\t */\n\t\tpublic static final String CASES_UPPER = \"upper\";\n\n\t\t/**\n\t\t * Constant for lower case.\n\t\t */\n\t\tpublic static final String CASES_LOWER = \"lower\";\n\n\t\t/**\n\t\t * Constant for keep case.\n\t\t */\n\t\tpublic static final String CASES_KEEP = \"keep\";\n\n\t\t/**\n\t\t * Constant configuration file name.\n\t\t */\n\t\tpublic static final String CONFIGURATION_FILE_NAME = \"litepal.xml\";\n\t}\n\n\tpublic interface TableSchema {\n\t\t/**\n\t\t * Table name in database.\n\t\t */\n\t\tpublic static final String TABLE_NAME = \"table_schema\";\n\n\t\t/**\n\t\t * The name column in table_schema.\n\t\t */\n\t\tpublic static final String COLUMN_NAME = \"name\";\n\n\t\t/**\n\t\t * The type column in table_schema.\n\t\t */\n\t\tpublic static final String COLUMN_TYPE = \"type\";\n\n\t\t/**\n\t\t * Constant for normal table.\n\t\t */\n\t\tpublic static final int NORMAL_TABLE = 0;\n\n\t\t/**\n\t\t * Constant for intermediate join table.\n\t\t */\n\t\tpublic static final int INTERMEDIATE_JOIN_TABLE = 1;\n\n        /**\n         * Constant for generic table.\n         */\n        public static final int GENERIC_TABLE = 2;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/util/DBUtility.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.util;\n\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.text.TextUtils;\nimport android.util.Pair;\n\nimport org.litepal.exceptions.DatabaseGenerateException;\nimport org.litepal.tablemanager.model.ColumnModel;\nimport org.litepal.tablemanager.model.TableModel;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * A utility class to help LitePal with some database actions. These actions can\n * help classes just do the jobs they care, and help them out of the trivial\n * work.\n * \n * @author Tony\n * @since 1.0\n */\npublic class DBUtility {\n\n    private static final String TAG = \"DBUtility\";\n\n    private static final String SQLITE_KEYWORDS = \",abort,add,after,all,alter,and,as,asc,autoincrement,before,begin,between,by,cascade,check,collate,column,commit,conflict,constraint,create,cross,database,deferrable,deferred,delete,desc,distinct,drop,each,end,escape,except,exclusive,exists,foreign,from,glob,group,having,in,index,inner,insert,intersect,into,is,isnull,join,like,limit,match,natural,not,notnull,null,of,offset,on,or,order,outer,plan,pragma,primary,query,raise,references,regexp,reindex,release,rename,replace,restrict,right,rollback,row,savepoint,select,set,table,temp,temporary,then,to,transaction,trigger,union,unique,update,using,vacuum,values,view,virtual,when,where,\";\n\n    private static final String KEYWORDS_COLUMN_SUFFIX = \"_lpcolumn\";\n\n    private static final String REG_OPERATOR = \"\\\\s*(=|!=|<>|<|>)\";\n\n    private static final String REG_FUZZY = \"\\\\s+(not\\\\s+)?(like|between)\\\\s+\";\n\n    private static final String REG_COLLECTION = \"\\\\s+(not\\\\s+)?(in)\\\\s*\\\\(\";\n\n    /**\n\t * Disable to create an instance of DBUtility.\n\t */\n\tprivate DBUtility() {\n\t}\n\n\t/**\n\t * Get the corresponding table name by the full class name with package. It\n\t * will only get the short class name without package name as table name.\n\t * \n\t * @param className\n\t *            Full class name with package.\n\t * @return Return the table name by getting the short class name. Return\n\t *         null if the class name is null or invalid.\n\t * \n\t */\n\tpublic static String getTableNameByClassName(String className) {\n\t\tif (!TextUtils.isEmpty(className)) {\n\t\t\tif ('.' == className.charAt(className.length() - 1)) {\n\t\t\t\treturn null;\n\t\t\t} else {\n                return className.substring(className.lastIndexOf(\".\") + 1);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get the index name by column name.\n\t * In LitePal index name always named like columnName_index.\n\t * @param tableName\n\t * \t\t\tTable name.\n\t * @param columnName\n\t * \t\t\tColumn name.\n\t * @return Index name or null if column name is null or empty.\n\t */\n\tpublic static String getIndexName(String tableName, String columnName) {\n\t\tif (!TextUtils.isEmpty(tableName) && !TextUtils.isEmpty(columnName)) {\n\t\t\treturn tableName + \"_\" + columnName + \"_index\";\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get the corresponding table name list by the full class name list with\n\t * package. Each table name will only get the short class name without\n\t * package.\n\t * \n\t * @param classNames\n\t *            The list of full class name with package.\n\t * @return Return the table name list.\n\t */\n\tpublic static List<String> getTableNameListByClassNameList(List<String> classNames) {\n\t\tList<String> tableNames = new ArrayList<String>();\n\t\tif (classNames != null && !classNames.isEmpty()) {\n\t\t\tfor (String className : classNames) {\n\t\t\t\ttableNames.add(getTableNameByClassName(className));\n\t\t\t}\n\t\t}\n\t\treturn tableNames;\n\t}\n\n\t/**\n\t * Get table name by the given foreign column name.\n\t * \n\t * @param foreignColumnName\n\t *            The foreign column name. Standard pattern is tablename_id.\n\t * @return The table name. Return null if the given foreign column is null,\n\t *         empty or invalid.\n\t */\n\tpublic static String getTableNameByForeignColumn(String foreignColumnName) {\n\t\tif (!TextUtils.isEmpty(foreignColumnName)) {\n\t\t\tif (foreignColumnName.toLowerCase(Locale.US).endsWith(\"_id\")) {\n\t\t\t\treturn foreignColumnName.substring(0, foreignColumnName.length() - \"_id\".length());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Create intermediate join table name by the concatenation of the two\n\t * target table names in alphabetical order with underline in the middle.\n\t * \n\t * @param tableName\n\t *            First table name.\n\t * @param associatedTableName\n\t *            The associated table name.\n\t * @return The table name by the concatenation of the two target table names\n\t *         in alphabetical order with underline in the middle. If the table\n\t *         name or associated table name is null of empty, return null.\n\t */\n\tpublic static String getIntermediateTableName(String tableName, String associatedTableName) {\n        if (!(TextUtils.isEmpty(tableName) || TextUtils.isEmpty(associatedTableName))) {\n            String intermediateTableName;\n            if (tableName.toLowerCase(Locale.US).compareTo(associatedTableName.toLowerCase(Locale.US)) <= 0) {\n                intermediateTableName = tableName + \"_\" + associatedTableName;\n            } else {\n                intermediateTableName = associatedTableName + \"_\" + tableName;\n            }\n            return intermediateTableName;\n        }\n        return null;\n\t}\n\n    /**\n     * Create generic table name by the concatenation of the class model's table name and simple\n     * generic type name with underline in the middle.\n     * @param className\n     *          Name of the class model.\n     * @param fieldName\n     *          Name of the generic type field.\n     * @return Table name by the concatenation of the class model's table name and simple\n     *         generic type name with underline in the middle.\n     */\n    public static String getGenericTableName(String className, String fieldName) {\n        String tableName = getTableNameByClassName(className);\n        return BaseUtility.changeCase(tableName + \"_\" + fieldName);\n    }\n\n    /**\n     * The column name for referenced id in generic table.\n     * @param className\n     *          Name of the class model.\n     * @return The column name for referenced id in generic table.\n     */\n    public static String getGenericValueIdColumnName(String className) {\n        return BaseUtility.changeCase(getTableNameByClassName(className) + \"_id\");\n    }\n\n    public static String getM2MSelfRefColumnName(Field field) {\n        return BaseUtility.changeCase(field.getName() + \"_id\");\n    }\n\n\t/**\n\t * Judge the table name is an intermediate table or not.\n\t * \n\t * @param tableName\n\t *            Table name in database.\n\t * @return Return true if the table name is an intermediate table. Otherwise\n\t *         return false.\n\t */\n\tpublic static boolean isIntermediateTable(String tableName, SQLiteDatabase db) {\n\t\tif (!TextUtils.isEmpty(tableName)) {\n\t\t\tif (tableName.matches(\"[0-9a-zA-Z]+_[0-9a-zA-Z]+\")) {\n\t\t\t\tCursor cursor = null;\n\t\t\t\ttry {\n\t\t\t\t\tcursor = db.query(Const.TableSchema.TABLE_NAME, null, null, null, null, null,\n\t\t\t\t\t\t\tnull);\n\t\t\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\tString tableNameDB = cursor.getString(cursor\n\t\t\t\t\t\t\t\t\t.getColumnIndexOrThrow(Const.TableSchema.COLUMN_NAME));\n\t\t\t\t\t\t\tif (tableName.equalsIgnoreCase(tableNameDB)) {\n\t\t\t\t\t\t\t\tint tableType = cursor.getInt(cursor\n\t\t\t\t\t\t\t\t\t\t.getColumnIndexOrThrow(Const.TableSchema.COLUMN_TYPE));\n\t\t\t\t\t\t\t\tif (tableType == Const.TableSchema.INTERMEDIATE_JOIN_TABLE) {\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} while (cursor.moveToNext());\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t} finally {\n\t\t\t\t\tif (cursor != null) {\n\t\t\t\t\t\tcursor.close();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n    /**\n     * Judge the table name is an generic table or not.\n     *\n     * @param tableName\n     *            Table name in database.\n     * @return Return true if the table name is an generic table. Otherwise\n     *         return false.\n     */\n    public static boolean isGenericTable(String tableName, SQLiteDatabase db) {\n        if (!TextUtils.isEmpty(tableName)) {\n            if (tableName.matches(\"[0-9a-zA-Z]+_[0-9a-zA-Z]+\")) {\n                Cursor cursor = null;\n                try {\n                    cursor = db.query(Const.TableSchema.TABLE_NAME, null, null, null, null, null,\n                            null);\n                    if (cursor.moveToFirst()) {\n                        do {\n                            String tableNameDB = cursor.getString(cursor\n                                    .getColumnIndexOrThrow(Const.TableSchema.COLUMN_NAME));\n                            if (tableName.equalsIgnoreCase(tableNameDB)) {\n                                int tableType = cursor.getInt(cursor\n                                        .getColumnIndexOrThrow(Const.TableSchema.COLUMN_TYPE));\n                                if (tableType == Const.TableSchema.GENERIC_TABLE) {\n                                    return true;\n                                }\n                                break;\n                            }\n                        } while (cursor.moveToNext());\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                } finally {\n                    if (cursor != null) {\n                        cursor.close();\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n\t/**\n\t * Test if the table name passed in exists in the database. Cases are\n\t * ignored.\n\t * \n\t * @param tableName\n\t *            The table name.\n\t * @return Return true if there's already a same name table exist, otherwise\n\t *         return false.\n\t */\n\tpublic static boolean isTableExists(String tableName, SQLiteDatabase db) {\n\t\tboolean exist;\n\t\ttry {\n\t\t\texist = BaseUtility.containsIgnoreCases(findAllTableNames(db), tableName);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\texist = false;\n\t\t}\n\t\treturn exist;\n\t}\n\n\t/**\n\t * Test if a column exists in a table. Cases are ignored.\n\t * \n\t * @param columnName\n\t *            The column name.\n\t * @param tableName\n\t *            The table name.\n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @return If there's a column named as the column name passed in, return\n\t *         true. Or return false. If any of the passed in parameters is null\n\t *         or empty, return false.\n\t */\n\tpublic static boolean isColumnExists(String columnName, String tableName, SQLiteDatabase db) {\n\t\tif (TextUtils.isEmpty(columnName) || TextUtils.isEmpty(tableName)) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean exist = false;\n        Cursor cursor = null;\n\t\ttry {\n            String checkingColumnSQL = \"pragma table_info(\" + tableName + \")\";\n            cursor = db.rawQuery(checkingColumnSQL, null);\n            if (cursor.moveToFirst()) {\n                do {\n                    String name = cursor.getString(cursor.getColumnIndexOrThrow(\"name\"));\n                    if (columnName.equalsIgnoreCase(name)) {\n                        exist = true;\n                        break;\n                    }\n                } while (cursor.moveToNext());\n            }\n\t\t} catch (Exception e) {\n            e.printStackTrace();\n            exist = false;\n        } finally {\n            if (cursor != null) {\n                cursor.close();\n            }\n        }\n\t\treturn exist;\n\t}\n\n\t/**\n\t * Find all table names in the database. If there's some wrong happens when\n\t * finding tables, it will throw exceptions.\n\t * \n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @return A list with all table names.\n\t * @throws org.litepal.exceptions.DatabaseGenerateException\n\t */\n\tpublic static List<String> findAllTableNames(SQLiteDatabase db) {\n\t\tList<String> tableNames = new ArrayList<String>();\n\t\tCursor cursor = null;\n\t\ttry {\n\t\t\tcursor = db.rawQuery(\"select * from sqlite_master where type = ?\", new String[] { \"table\" });\n\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\tdo {\n\t\t\t\t\tString tableName = cursor.getString(cursor.getColumnIndexOrThrow(\"tbl_name\"));\n\t\t\t\t\tif (!tableNames.contains(tableName)) {\n\t\t\t\t\t\ttableNames.add(tableName);\n\t\t\t\t\t}\n\t\t\t\t} while (cursor.moveToNext());\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tthrow new DatabaseGenerateException(e.getMessage());\n\t\t} finally {\n\t\t\tif (cursor != null) {\n\t\t\t\tcursor.close();\n\t\t\t}\n\t\t}\n\t\treturn tableNames;\n\t}\n\n\t/**\n\t * Look from the database to find a table named same as the table name in\n\t * table model. Then iterate the columns and types of this table to create a\n\t * new instance of table model. If there's no such a table in the database,\n\t * then throw DatabaseGenerateException.\n\t * \n\t * @param tableName\n\t *            Table name.\n\t * @param db\n\t *            Instance of SQLiteDatabase.\n\t * @return A table model object with values from database table.\n\t * @throws org.litepal.exceptions.DatabaseGenerateException\n\t */\n\tpublic static TableModel findPragmaTableInfo(String tableName, SQLiteDatabase db) {\n\t\tif (isTableExists(tableName, db)) {\n\t\t\tPair<Set<String>, Set<String>> indexPair = findIndexedColumns(tableName, db);\n\t\t\tSet<String> indexColumns = indexPair.first;\n\t\t\tSet<String> uniqueColumns = indexPair.second;\n\t\t\tTableModel tableModelDB = new TableModel();\n\t\t\ttableModelDB.setTableName(tableName);\n\t\t\tString checkingColumnSQL = \"pragma table_info(\" + tableName + \")\";\n\t\t\tCursor cursor = null;\n\t\t\ttry {\n\t\t\t\tcursor = db.rawQuery(checkingColumnSQL, null);\n\t\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\t\tdo {\n                        ColumnModel columnModel = new ColumnModel();\n                        String name = cursor.getString(cursor.getColumnIndexOrThrow(\"name\"));\n                        String type = cursor.getString(cursor.getColumnIndexOrThrow(\"type\"));\n                        boolean nullable = cursor.getInt(cursor.getColumnIndexOrThrow(\"notnull\")) != 1;\n                        boolean unique = uniqueColumns.contains(name);\n                        boolean hasIndex = indexColumns.contains(name);\n                        String defaultValue = cursor.getString(cursor.getColumnIndexOrThrow(\"dflt_value\"));\n                        columnModel.setColumnName(name);\n                        columnModel.setColumnType(type);\n                        columnModel.setNullable(nullable);\n                        columnModel.setUnique(unique);\n                        columnModel.setHasIndex(hasIndex);\n                        if (defaultValue != null) {\n                            defaultValue = defaultValue.replace(\"'\", \"\");\n                        } else {\n                            defaultValue = \"\";\n                        }\n                        columnModel.setDefaultValue(defaultValue);\n\t\t\t\t\t\ttableModelDB.addColumnModel(columnModel);\n\t\t\t\t\t} while (cursor.moveToNext());\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tthrow new DatabaseGenerateException(e.getMessage());\n\t\t\t} finally {\n\t\t\t\tif (cursor != null) {\n\t\t\t\t\tcursor.close();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn tableModelDB;\n\t\t} else {\n\t\t\tthrow new DatabaseGenerateException(\n\t\t\t\t\tDatabaseGenerateException.TABLE_DOES_NOT_EXIST_WHEN_EXECUTING + tableName);\n\t\t}\n\t}\n\n    /**\n     * Find all columns with index, including normal index and unique index of specified table.\n     * @param tableName\n     *          The table to find unique columns.\n     * @param db\n     *          Instance of SQLiteDatabase.\n     * @return A pair contains two types of index columns. First is normal index columns. Second is unique index columns.\n     */\n    public static Pair<Set<String>, Set<String>> findIndexedColumns(String tableName, SQLiteDatabase db) {\n\t\tSet<String> indexColumns = new HashSet<>();\n\t\tSet<String> uniqueColumns = new HashSet<>();\n        Cursor cursor = null;\n        Cursor innerCursor = null;\n        try {\n            cursor = db.rawQuery(\"pragma index_list(\" + tableName +\")\", null);\n            if (cursor.moveToFirst()) {\n                do {\n                    boolean unique = cursor.getInt(cursor.getColumnIndexOrThrow(\"unique\")) == 1;\n\t\t\t\t\tString name = cursor.getString(cursor.getColumnIndexOrThrow(\"name\"));\n\t\t\t\t\tinnerCursor = db.rawQuery(\"pragma index_info(\" + name + \")\", null);\n\t\t\t\t\tif (innerCursor.moveToFirst()) {\n\t\t\t\t\t\tString columnName = innerCursor.getString(innerCursor.getColumnIndexOrThrow(\"name\"));\n\t\t\t\t\t\tif (unique) {\n\t\t\t\t\t\t\tuniqueColumns.add(columnName);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tindexColumns.add(columnName);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n                } while (cursor.moveToNext());\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n            throw new DatabaseGenerateException(e.getMessage());\n        } finally {\n            if (cursor != null) {\n                cursor.close();\n            }\n            if (innerCursor != null) {\n                innerCursor.close();\n            }\n        }\n        return new Pair<>(indexColumns, uniqueColumns);\n    }\n\n    /**\n     * If the field name is conflicted with SQLite keywords. Return true if conflicted, return false\n     * otherwise.\n     * @param fieldName\n     *          Name of the field.\n     * @return True if conflicted, false otherwise.\n     */\n    public static boolean isFieldNameConflictWithSQLiteKeywords(String fieldName) {\n        if (!TextUtils.isEmpty(fieldName)) {\n            String fieldNameWithComma = \",\" + fieldName.toLowerCase(Locale.US) + \",\";\n\t\t\treturn SQLITE_KEYWORDS.contains(fieldNameWithComma);\n        }\n        return false;\n    }\n\n    /**\n     * Convert the passed in name to valid column name if the name is conflicted with SQLite keywords.\n     * The convert rule is to append {@link #KEYWORDS_COLUMN_SUFFIX} to the name as new column name.\n     * @param columnName\n     *          Original column name.\n     * @return Converted name as new column name if conflicted with SQLite keywords.\n     */\n    public static String convertToValidColumnName(String columnName) {\n        if (isFieldNameConflictWithSQLiteKeywords(columnName)) {\n            return columnName + KEYWORDS_COLUMN_SUFFIX;\n        }\n        return columnName;\n    }\n\n    /**\n     * Convert the where clause if it contains invalid column names which conflict with SQLite keywords.\n     * @param whereClause\n     *          where clause for query, update or delete.\n     * @return Converted where clause with valid column names.\n     */\n    public static String convertWhereClauseToColumnName(String whereClause) {\n        if (!TextUtils.isEmpty(whereClause)) {\n            try {\n                StringBuffer convertedWhereClause = new StringBuffer();\n                Pattern p = Pattern.compile(\"(\\\\w+\" + REG_OPERATOR + \"|\\\\w+\" + REG_FUZZY + \"|\\\\w+\" + REG_COLLECTION + \")\");\n                Matcher m = p.matcher(whereClause);\n                while (m.find()) {\n                    String matches = m.group();\n                    String column = matches.replaceAll(\"(\" + REG_OPERATOR + \"|\" + REG_FUZZY + \"|\" + REG_COLLECTION + \")\", \"\");\n                    String rest = matches.replace(column, \"\");\n                    column = convertToValidColumnName(column);\n                    m.appendReplacement(convertedWhereClause, column + rest);\n                }\n                m.appendTail(convertedWhereClause);\n                return convertedWhereClause.toString();\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n        return whereClause;\n    }\n\n    /**\n     * Convert the select clause if it contains invalid column names which conflict with SQLite keywords.\n     * @param columns\n     *          A String array of which columns to return. Passing null will\n     *          return all columns.\n     * @return Converted select clause with valid column names.\n     */\n    public static String[] convertSelectClauseToValidNames(String[] columns) {\n        if (columns != null && columns.length > 0) {\n            String[] convertedColumns = new String[columns.length];\n            for (int i = 0; i < columns.length; i++) {\n                convertedColumns[i] = convertToValidColumnName(columns[i]);\n            }\n            return convertedColumns;\n        }\n        return null;\n    }\n\n    /**\n     * Convert the order by clause if it contains invalid column names which conflict with SQLite keywords.\n     * @param orderBy\n     *          How to order the rows, formatted as an SQL ORDER BY clause. Passing null will use\n     *          the default sort order, which may be unordered.\n     * @return Converted order by clause with valid column names.\n     */\n    public static String convertOrderByClauseToValidName(String orderBy) {\n        if (!TextUtils.isEmpty(orderBy)) {\n            orderBy = orderBy.trim().toLowerCase(Locale.US);\n            if (orderBy.contains(\",\")) {\n                String[] orderByItems = orderBy.split(\",\");\n                StringBuilder builder = new StringBuilder();\n                boolean needComma = false;\n                for (String orderByItem : orderByItems) {\n                    if (needComma) {\n                        builder.append(\",\");\n                    }\n                    builder.append(convertOrderByItem(orderByItem));\n                    needComma = true;\n                }\n                orderBy = builder.toString();\n            } else {\n                orderBy = convertOrderByItem(orderBy);\n            }\n            return orderBy;\n        }\n        return null;\n    }\n\n    /**\n     * Convert the order by item if it is invalid column name which conflict with SQLite keywords.\n     * @param orderByItem\n     *          The single order by condition.\n     * @return Converted order by item with valid column name.\n     */\n    private static String convertOrderByItem(String orderByItem) {\n        String column;\n        String append;\n        if (orderByItem.endsWith(\"asc\")) {\n            column = orderByItem.replace(\"asc\", \"\").trim();\n            append = \" asc\";\n        } else if (orderByItem.endsWith(\"desc\")) {\n            column = orderByItem.replace(\"desc\", \"\").trim();\n            append = \" desc\";\n        } else {\n            column = orderByItem;\n            append = \"\";\n        }\n        return convertToValidColumnName(column) + append;\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/util/LitePalLog.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.util;\n\nimport android.util.Log;\n\npublic final class LitePalLog {\n\t\n\tpublic static final int DEBUG = 2;\n\t\n\tpublic static final int ERROR = 5;\n\t\n\tpublic static int level = ERROR;\n\t\n\tpublic static void d(String tagName, String message) {\n\t\tif (level <= DEBUG) {\n\t\t\tLog.d(tagName, message);\n\t\t}\n\t}\n\t\n\tpublic static void e(String tagName, Exception e){\n\t\tif (level <= ERROR) {\n\t\t\tLog.e(tagName, e.getMessage(), e);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "core/src/main/java/org/litepal/util/SharedUtil.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.util;\n\nimport org.litepal.LitePalApplication;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.text.TextUtils;\n\n/**\n * LitePal used shared preferences a lot for storing versions and a lot of other\n * necessary values. This utility helps LitePal save and read each key-value\n * pairs from shared preferences file.\n * \n * @author Tony Green\n * @since 1.0\n */\npublic class SharedUtil {\n\n\tprivate static final String VERSION = \"litepal_version\";\n\n\tprivate static final String LITEPAL_PREPS = \"litepal_prefs\";\n\n\t/**\n\t * Each time database upgrade, the version of database stored in shared\n\t * preference will update.\n\t * @param extraKeyName\n\t * \t\t\tPass the name of the using database usually. Pass null if it's default database.\n\t * @param newVersion\n     *          new version of database\n\t */\n\tpublic static void updateVersion(String extraKeyName, int newVersion) {\n\t\tSharedPreferences.Editor sEditor = LitePalApplication.getContext()\n\t\t\t\t.getSharedPreferences(LITEPAL_PREPS, Context.MODE_PRIVATE).edit();\n\t\tif (TextUtils.isEmpty(extraKeyName)) {\n\t\t\tsEditor.putInt(VERSION, newVersion);\n\t\t} else {\n            if (extraKeyName.endsWith(Const.Config.DB_NAME_SUFFIX)) {\n                extraKeyName = extraKeyName.replace(Const.Config.DB_NAME_SUFFIX, \"\");\n            }\n\t\t\tsEditor.putInt(VERSION + \"_\" + extraKeyName, newVersion);\n\t\t}\n\t\tsEditor.apply();\n\t}\n\n\t/**\n\t * Get the last database version.\n\t * @param extraKeyName\n\t * \t\t\tPass the name of the using database usually. Pass null if it's default database.\n\t * @return the last database version\n\t */\n\tpublic static int getLastVersion(String extraKeyName) {\n\t\tSharedPreferences sPref = LitePalApplication.getContext().getSharedPreferences(\n\t\t\t\tLITEPAL_PREPS, Context.MODE_PRIVATE);\n\t\tif (TextUtils.isEmpty(extraKeyName)) {\n\t\t\treturn sPref.getInt(VERSION, 0);\n\t\t} else {\n            if (extraKeyName.endsWith(Const.Config.DB_NAME_SUFFIX)) {\n                extraKeyName = extraKeyName.replace(Const.Config.DB_NAME_SUFFIX, \"\");\n            }\n\t\t\treturn sPref.getInt(VERSION + \"_\" + extraKeyName, 0);\n\t\t}\n\t}\n\n    /**\n     * Remove the version with specified extra key name.\n     * @param extraKeyName\n     * \t\t\tPass the name of the using database usually. Pass null if it's default database.\n     */\n    public static void removeVersion(String extraKeyName) {\n        SharedPreferences.Editor sEditor = LitePalApplication.getContext()\n                .getSharedPreferences(LITEPAL_PREPS, Context.MODE_PRIVATE).edit();\n        if (TextUtils.isEmpty(extraKeyName)) {\n            sEditor.remove(VERSION);\n        } else {\n            if (extraKeyName.endsWith(Const.Config.DB_NAME_SUFFIX)) {\n                extraKeyName = extraKeyName.replace(Const.Config.DB_NAME_SUFFIX, \"\");\n            }\n            sEditor.remove(VERSION + \"_\" + extraKeyName);\n        }\n        sEditor.apply();\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/util/cipher/AESCrypt.java",
    "content": "/*\n *   Copyright (c) 2014 Scott Alexander-Bown\n *\n *   Licensed under the Apache License, Version 2.0 (the \"License\");\n *   you may not use this file except in compliance with the License.\n *   You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n *   Unless required by applicable law or agreed to in writing, software\n *   distributed under the License is distributed on an \"AS IS\" BASIS,\n *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *   See the License for the specific language governing permissions and\n *   limitations under the License.\n */\npackage org.litepal.util.cipher;\n\nimport android.util.Base64;\nimport android.util.Log;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.GeneralSecurityException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\n\n/**\n * Encrypt and decrypt messages using AES 256 bit encryption that are compatible with AESCrypt-ObjC and AESCrypt Ruby.\n * <p/>\n * Created by scottab on 04/10/2014.\n */\npublic final class AESCrypt {\n\n    private static final String TAG = \"AESCrypt\";\n\n    //AESCrypt-ObjC uses CBC and PKCS7Padding\n    private static final String AES_MODE = \"AES/CBC/PKCS7Padding\";\n    private static final String CHARSET = \"UTF-8\";\n\n    //AESCrypt-ObjC uses SHA-256 (and so a 256-bit key)\n    private static final String HASH_ALGORITHM = \"SHA-256\";\n\n    //AESCrypt-ObjC uses blank IV (not the best security, but the aim here is compatibility)\n    private static final byte[] ivBytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};\n\n    //togglable log option (please turn off in live!)\n    public static boolean DEBUG_LOG_ENABLED = false;\n\n\n    /**\n     * Generates SHA256 hash of the password which is used as key\n     *\n     * @param password used to generated key\n     * @return SHA256 of the password\n     */\n    private static SecretKeySpec generateKey(final String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {\n        final MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);\n        byte[] bytes = password.getBytes(\"UTF-8\");\n        digest.update(bytes, 0, bytes.length);\n        byte[] key = digest.digest();\n\n        log(\"SHA-256 key \", key);\n\n        SecretKeySpec secretKeySpec = new SecretKeySpec(key, \"AES\");\n        return secretKeySpec;\n    }\n\n\n    /**\n     * Encrypt and encode message using 256-bit AES with key generated from password.\n     *\n     *\n     * @param password used to generated key\n     * @param message the thing you want to encrypt assumed String UTF-8\n     * @return Base64 encoded CipherText\n     * @throws GeneralSecurityException if problems occur during encryption\n     */\n    public static String encrypt(final String password, String message)\n            throws GeneralSecurityException {\n\n        try {\n            final SecretKeySpec key = generateKey(password);\n\n            log(\"message\", message);\n\n            byte[] cipherText = encrypt(key, ivBytes, message.getBytes(CHARSET));\n\n            //NO_WRAP is important as was getting \\n at the end\n            String encoded = Base64.encodeToString(cipherText, Base64.NO_WRAP);\n            log(\"Base64.NO_WRAP\", encoded);\n            return encoded;\n        } catch (UnsupportedEncodingException e) {\n            if (DEBUG_LOG_ENABLED)\n                Log.e(TAG, \"UnsupportedEncodingException \", e);\n            throw new GeneralSecurityException(e);\n        }\n    }\n\n\n    /**\n     * More flexible AES encrypt that doesn't encode\n     * @param key AES key typically 128, 192 or 256 bit\n     * @param iv Initiation Vector\n     * @param message in bytes (assumed it's already been decoded)\n     * @return Encrypted cipher text (not encoded)\n     * @throws GeneralSecurityException if something goes wrong during encryption\n     */\n    public static byte[] encrypt(final SecretKeySpec key, final byte[] iv, final byte[] message)\n            throws GeneralSecurityException {\n        final Cipher cipher = Cipher.getInstance(AES_MODE);\n        IvParameterSpec ivSpec = new IvParameterSpec(iv);\n        cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);\n        byte[] cipherText = cipher.doFinal(message);\n\n        log(\"cipherText\", cipherText);\n\n        return cipherText;\n    }\n\n\n    /**\n     * Decrypt and decode ciphertext using 256-bit AES with key generated from password\n     *\n     * @param password used to generated key\n     * @param base64EncodedCipherText the encrpyted message encoded with base64\n     * @return message in Plain text (String UTF-8)\n     * @throws GeneralSecurityException if there's an issue decrypting\n     */\n    public static String decrypt(final String password, String base64EncodedCipherText)\n            throws GeneralSecurityException {\n\n        try {\n            final SecretKeySpec key = generateKey(password);\n\n            log(\"base64EncodedCipherText\", base64EncodedCipherText);\n            byte[] decodedCipherText = Base64.decode(base64EncodedCipherText, Base64.NO_WRAP);\n            log(\"decodedCipherText\", decodedCipherText);\n\n            byte[] decryptedBytes = decrypt(key, ivBytes, decodedCipherText);\n\n            log(\"decryptedBytes\", decryptedBytes);\n            String message = new String(decryptedBytes, CHARSET);\n            log(\"message\", message);\n\n\n            return message;\n        } catch (UnsupportedEncodingException e) {\n            if (DEBUG_LOG_ENABLED)\n                Log.e(TAG, \"UnsupportedEncodingException \", e);\n\n            throw new GeneralSecurityException(e);\n        }\n    }\n\n\n    /**\n     * More flexible AES decrypt that doesn't encode\n     *\n     * @param key AES key typically 128, 192 or 256 bit\n     * @param iv Initiation Vector\n     * @param decodedCipherText in bytes (assumed it's already been decoded)\n     * @return Decrypted message cipher text (not encoded)\n     * @throws GeneralSecurityException if something goes wrong during encryption\n     */\n    public static byte[] decrypt(final SecretKeySpec key, final byte[] iv, final byte[] decodedCipherText)\n            throws GeneralSecurityException {\n            final Cipher cipher = Cipher.getInstance(AES_MODE);\n            IvParameterSpec ivSpec = new IvParameterSpec(iv);\n            cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);\n            byte[] decryptedBytes = cipher.doFinal(decodedCipherText);\n\n            log(\"decryptedBytes\", decryptedBytes);\n\n            return decryptedBytes;\n    }\n\n\n\n\n    private static void log(String what, byte[] bytes) {\n        if (DEBUG_LOG_ENABLED)\n            Log.d(TAG, what + \"[\" + bytes.length + \"] [\" + bytesToHex(bytes) + \"]\");\n    }\n\n    private static void log(String what, String value) {\n        if (DEBUG_LOG_ENABLED)\n            Log.d(TAG, what + \"[\" + value.length() + \"] [\" + value + \"]\");\n    }\n\n\n    /**\n     * Converts byte array to hexidecimal useful for logging and fault finding\n     * @param bytes\n     * @return\n     */\n    private static String bytesToHex(byte[] bytes) {\n        final char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8',\n                '9', 'A', 'B', 'C', 'D', 'E', 'F'};\n        char[] hexChars = new char[bytes.length * 2];\n        int v;\n        for (int j = 0; j < bytes.length; j++) {\n            v = bytes[j] & 0xFF;\n            hexChars[j * 2] = hexArray[v >>> 4];\n            hexChars[j * 2 + 1] = hexArray[v & 0x0F];\n        }\n        return new String(hexChars);\n    }\n\n    private AESCrypt() {\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/org/litepal/util/cipher/CipherUtil.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.util.cipher;\n\nimport android.text.TextUtils;\nimport android.util.Base64;\nimport android.util.Log;\n\nimport java.nio.charset.Charset;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.KeyGenerator;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\n\n/**\n * Utility to manage encryption and decryption for different algorithms.\n *\n * @author Tony Green\n * @since 1.6\n */\npublic class CipherUtil {\n\n    private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};\n\n    public static String aesKey = \"LitePalKey\";\n\n    /**\n     * Encrypt the plain text with AES algorithm.\n     * @param plainText\n     *          The plain text.\n     * @return The Encrypt content.\n     */\n    public static String aesEncrypt(String plainText) {\n        if (TextUtils.isEmpty(plainText)) {\n            return plainText;\n        }\n        try {\n            return AESCrypt.encrypt(aesKey, plainText);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * Decrypt the encrypted text with AES algorithm.\n     * @param encryptedText\n     *          The encrypted text.\n     * @return The plain content.\n     */\n    public static String aesDecrypt(String encryptedText) {\n        if (TextUtils.isEmpty(encryptedText)) {\n            return encryptedText;\n        }\n        try {\n            return AESCrypt.decrypt(aesKey, encryptedText);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * Encrypt the plain text with MD5 algorithm.\n     * @param plainText\n     *          The plain text.\n     * @return The Encrypt content.\n     */\n    public static String md5Encrypt(String plainText) {\n        try {\n            MessageDigest digest = MessageDigest.getInstance(\"MD5\");\n            digest.update(plainText.getBytes(Charset.defaultCharset()));\n            return new String(toHex(digest.digest()));\n        } catch (NoSuchAlgorithmException e) {\n            e.printStackTrace();\n        }\n        return \"\";\n    }\n\n    private static char[] toHex(byte[] data) {\n        char[] toDigits = DIGITS_UPPER;\n        int l = data.length;\n        char[] out = new char[l << 1];\n        // two characters form the hex value.\n        for (int i = 0, j = 0; i < l; i++) {\n            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];\n            out[j++] = toDigits[0x0F & data[i]];\n        }\n        return out;\n    }\n\n}"
  },
  {
    "path": "core/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">LitePal</string>\n</resources>\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Jan 29 20:32:21 CST 2021\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.0.2-bin.zip\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "java/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "java/bintray.gradle",
    "content": "group = PROJ_GROUP\nversion = PROJ_VERSION\nproject.archivesBaseName = PROJ_ARTIFACTID_JAVA\n\napply plugin: 'com.jfrog.bintray'\napply plugin: 'maven-publish'\n\ntask sourcesJar(type: Jar) {\n    from android.sourceSets.main.java.srcDirs\n    classifier = 'sources'\n}\n\nartifacts {\n    archives sourcesJar\n}\n\ndef pomConfig = {\n    licenses {\n        license {\n            name \"The Apache Software License, Version 2.0\"\n            url \"http://www.apache.org/licenses/LICENSE-2.0.txt\"\n            distribution \"repo\"\n        }\n    }\n    developers {\n        developer {\n            id DEVELOPER_ID\n            name DEVELOPER_NAME\n            email DEVELOPER_EMAIL\n        }\n    }\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            artifactId PROJ_ARTIFACTID_JAVA\n\n            pom{\n                packaging 'aar'\n            }\n//            pom.withXml {\n//                def root = asNode()\n//                root.appendNode('description', PROJ_DESCRIPTION)\n//                root.children().last() + pomConfig\n//            }\n            //The publication doesn't know about our dependencies, so we have to manually add them to the pom\n            pom.withXml {\n                def dependenciesNode = asNode().appendNode('dependencies')\n\n                //Iterate over the compile dependencies (we don't want the test ones), adding a <dependency> node for each\n                configurations.api.allDependencies.each {\n                    def dependencyNode = dependenciesNode.appendNode('dependency')\n                    dependencyNode.appendNode('groupId', it.group)\n                    dependencyNode.appendNode('artifactId', it.name)\n                    dependencyNode.appendNode('version', it.version)\n                }\n            }\n        }\n    }\n}\n\nbintray {\n    user = BINTRAY_USER\n    key = BINTRAY_KEY\n\n    configurations = ['archives']\n    publications = ['mavenJava']\n    publish = true\n\n    pkg {\n        repo = 'maven'\n        name = PROJ_NAME_JAVA\n        desc = PROJ_DESCRIPTION\n        websiteUrl = PROJ_WEBSITEURL\n        vcsUrl = PROJ_VCSURL\n        licenses = ['Apache-2.0']\n        publicDownloadNumbers = true\n    }\n}"
  },
  {
    "path": "java/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 28\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 28\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    api project(':core')\n}\n\nif (hasProperty(\"BINTRAY_KEY\")) {\n    apply from: 'bintray.gradle'\n}"
  },
  {
    "path": "java/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /home/tony/Android/Sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "java/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"org.litepal.java\">\n\n    <application>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "java/src/main/java/org/litepal/LitePal.java",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\n\nimport org.litepal.crud.LitePalSupport;\nimport org.litepal.crud.async.AverageExecutor;\nimport org.litepal.crud.async.CountExecutor;\nimport org.litepal.crud.async.FindExecutor;\nimport org.litepal.crud.async.FindMultiExecutor;\nimport org.litepal.crud.async.SaveExecutor;\nimport org.litepal.crud.async.UpdateOrDeleteExecutor;\nimport org.litepal.tablemanager.callback.DatabaseListener;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * LitePal is an Android library that allows developers to use SQLite database extremely easy.\n * You can initialized it by calling {@link #initialize(Context)} method to make LitePal ready to\n * work. Also you can switch the using database by calling {@link #use(LitePalDB)} and {@link #useDefault()}\n * methods.\n *\n * @author Tony Green\n * @since 1.4\n */\npublic class LitePal {\n\n    /**\n     * Initialize to make LitePal ready to work. If you didn't configure LitePalApplication\n     * in the AndroidManifest.xml, make sure you call this method as soon as possible. In\n     * Application's onCreate() method will be fine.\n     *\n     * @param context\n     * \t\tApplication context.\n     */\n    public static void initialize(Context context) {\n        Operator.initialize(context);\n    }\n\n    /**\n     * Get a writable SQLiteDatabase.\n     *\n     * @return A writable SQLiteDatabase instance\n     */\n    public static SQLiteDatabase getDatabase() {\n        return Operator.getDatabase();\n    }\n\n    /**\n     * Switch the using database to the one specified by parameter.\n     * @param litePalDB\n     *          The database to switch to.\n     */\n    public static void use(LitePalDB litePalDB) {\n        Operator.use(litePalDB);\n    }\n\n    /**\n     * Switch the using database to default with configuration by litepal.xml.\n     */\n    public static void useDefault() {\n        Operator.useDefault();\n    }\n\n    /**\n     * Delete the specified database.\n     * @param dbName\n     *          Name of database to delete.\n     * @return True if delete success, false otherwise.\n     */\n    public static boolean deleteDatabase(String dbName) {\n        return Operator.deleteDatabase(dbName);\n    }\n\n    public static void aesKey(String key) {\n        Operator.aesKey(key);\n    }\n\n    /**\n     * Declaring to query which columns in table.\n     *\n     * <pre>\n     * LitePal.select(&quot;name&quot;, &quot;age&quot;).find(Person.class);\n     * </pre>\n     *\n     * This will find all rows with name and age columns in Person table.\n     *\n     * @param columns\n     *            A String array of which columns to return. Passing null will\n     *            return all columns.\n     *\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery select(String... columns) {\n        return Operator.select(columns);\n    }\n\n    /**\n     * Declaring to query which rows in table.\n     *\n     * <pre>\n     * LitePal.where(&quot;name = ? or age &gt; ?&quot;, &quot;Tom&quot;, &quot;14&quot;).find(Person.class);\n     * </pre>\n     *\n     * This will find rows which name is Tom or age greater than 14 in Person\n     * table.\n     *\n     * @param conditions\n     *            A filter declaring which rows to return, formatted as an SQL\n     *            WHERE clause. Passing null will return all rows.\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery where(String... conditions) {\n        return Operator.where(conditions);\n    }\n\n    /**\n     * Declaring how to order the rows queried from table.\n     *\n     * <pre>\n     * LitePal.order(&quot;name desc&quot;).find(Person.class);\n     * </pre>\n     *\n     * This will find all rows in Person table sorted by name with inverted\n     * order.\n     *\n     * @param column\n     *            How to order the rows, formatted as an SQL ORDER BY clause.\n     *            Passing null will use the default sort order, which may be\n     *            unordered.\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery order(String column) {\n        return Operator.order(column);\n    }\n\n    /**\n     * Limits the number of rows returned by the query.\n     *\n     * <pre>\n     * LitePal.limit(2).find(Person.class);\n     * </pre>\n     *\n     * This will find the top 2 rows in Person table.\n     *\n     * @param value\n     *            Limits the number of rows returned by the query, formatted as\n     *            LIMIT clause.\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery limit(int value) {\n        return Operator.limit(value);\n    }\n\n    /**\n     * Declaring the offset of rows returned by the query. This method must be\n     * used with {@link #limit(int)}, or nothing will return.\n     *\n     * <pre>\n     * LitePal.limit(1).offset(2).find(Person.class);\n     * </pre>\n     *\n     * This will find the third row in Person table.\n     *\n     * @param value\n     *            The offset amount of rows returned by the query.\n     * @return A FluentQuery instance.\n     */\n    public static FluentQuery offset(int value) {\n        return Operator.offset(value);\n    }\n\n    /**\n     * Count the records.\n     *\n     * <pre>\n     * LitePal.count(Person.class);\n     * </pre>\n     *\n     * This will count all rows in person table.<br>\n     * You can also specify a where clause when counting.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(Person.class);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @return Count of the specified table.\n     */\n    public static int count(Class<?> modelClass) {\n        return Operator.count(modelClass);\n    }\n\n    /**\n     * Basically same as {@link #count(Class)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *          Which table to query from by class.\n     * @return A CountExecutor instance.\n     */\n    public static CountExecutor countAsync(final Class<?> modelClass) {\n        return Operator.countAsync(modelClass);\n    }\n\n    /**\n     * Count the records.\n     *\n     * <pre>\n     * LitePal.count(&quot;person&quot;);\n     * </pre>\n     *\n     * This will count all rows in person table.<br>\n     * You can also specify a where clause when counting.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(&quot;person&quot;);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @return Count of the specified table.\n     */\n    public static int count(String tableName) {\n        return Operator.count(tableName);\n    }\n\n    /**\n     * Basically same as {@link #count(String)} but pending to a new thread for executing.\n     *\n     * @param tableName\n     *          Which table to query from.\n     * @return A CountExecutor instance.\n     */\n    public static CountExecutor countAsync(final String tableName) {\n        return Operator.countAsync(tableName);\n    }\n\n    /**\n     * Calculates the average value on a given column.\n     *\n     * <pre>\n     * LitePal.average(Person.class, &quot;age&quot;);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(Person.class, &quot;age&quot;);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param column\n     *            The based on column to calculate.\n     * @return The average value on a given column.\n     */\n    public static double average(Class<?> modelClass, String column) {\n        return Operator.average(modelClass, column);\n    }\n\n    /**\n     * Basically same as {@link #average(Class, String)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param column\n     *            The based on column to calculate.\n     * @return A AverageExecutor instance.\n     */\n    public static AverageExecutor averageAsync(final Class<?> modelClass, final String column) {\n        return Operator.averageAsync(modelClass, column);\n    }\n\n    /**\n     * Calculates the average value on a given column.\n     *\n     * <pre>\n     * LitePal.average(&quot;person&quot;, &quot;age&quot;);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(&quot;person&quot;, &quot;age&quot;);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param column\n     *            The based on column to calculate.\n     * @return The average value on a given column.\n     */\n    public static double average(String tableName, String column) {\n        return Operator.average(tableName, column);\n    }\n\n    /**\n     * Basically same as {@link #average(String, String)} but pending to a new thread for executing.\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param column\n     *            The based on column to calculate.\n     * @return A AverageExecutor instance.\n     */\n    public static AverageExecutor averageAsync(final String tableName, final String column) {\n        return Operator.averageAsync(tableName, column);\n    }\n\n    /**\n     * Calculates the maximum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.max(Person.class, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(Person.class, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The maximum value on a given column.\n     */\n    public static <T> T max(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return Operator.max(modelClass, columnName, columnType);\n    }\n\n    /**\n     * Basically same as {@link #max(Class, String, Class)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> maxAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return Operator.maxAsync(modelClass, columnName, columnType);\n    }\n\n    /**\n     * Calculates the maximum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.max(&quot;person&quot;, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The maximum value on a given column.\n     */\n    public static <T> T max(String tableName, String columnName, Class<T> columnType) {\n        return Operator.max(tableName, columnName, columnType);\n    }\n\n    /**\n     * Basically same as {@link #max(String, String, Class)} but pending to a new thread for executing.\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> maxAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        return Operator.maxAsync(tableName, columnName, columnType);\n    }\n\n    /**\n     * Calculates the minimum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.min(Person.class, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(Person.class, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The minimum value on a given column.\n     */\n    public static <T> T min(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return Operator.min(modelClass, columnName, columnType);\n    }\n\n    /**\n     * Basically same as {@link #min(Class, String, Class)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> minAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return Operator.minAsync(modelClass, columnName, columnType);\n    }\n\n    /**\n     * Calculates the minimum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.min(&quot;person&quot;, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The minimum value on a given column.\n     */\n    public static <T> T min(String tableName, String columnName, Class<T> columnType) {\n        return Operator.min(tableName, columnName, columnType);\n    }\n\n    /**\n     * Basically same as {@link #min(String, String, Class)} but pending to a new thread for executing.\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> minAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        return Operator.minAsync(tableName, columnName, columnType);\n    }\n\n    /**\n     * Calculates the sum of values on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.sum(Person.class, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(Person.class, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The sum value on a given column.\n     */\n    public static <T> T sum(Class<?> modelClass, String columnName, Class<T> columnType) {\n        return Operator.sum(modelClass, columnName, columnType);\n    }\n\n    /**\n     * Basically same as {@link #sum(Class, String, Class)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query from by class.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> sumAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {\n        return Operator.sumAsync(modelClass, columnName, columnType);\n    }\n\n    /**\n     * Calculates the sum of values on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * <pre>\n     * LitePal.sum(&quot;person&quot;, &quot;age&quot;, int.class);\n     * </pre>\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * <pre>\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     * </pre>\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return The sum value on a given column.\n     */\n    public static <T> T sum(String tableName, String columnName, Class<T> columnType) {\n        return Operator.sum(tableName, columnName, columnType);\n    }\n\n    /**\n     * Basically same as {@link #sum(String, String, Class)} but pending to a new thread for executing.\n     *\n     * @param tableName\n     *            Which table to query from.\n     * @param columnName\n     *            The based on column to calculate.\n     * @param columnType\n     *            The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> sumAsync(final String tableName, final String columnName, final Class<T> columnType) {\n        return Operator.sumAsync(tableName, columnName, columnType);\n    }\n\n    /**\n     * Finds the record by a specific id.\n     *\n     * <pre>\n     * Person p = LitePal.find(Person.class, 1);\n     * </pre>\n     *\n     * The modelClass determines which table to query and the object type to\n     * return. If no record can be found, then return null. <br>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link LitePal#find(Class, long, boolean)}.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param id\n     *            Which record to query.\n     * @return An object with found data from database, or null.\n     */\n    public static <T> T find(Class<T> modelClass, long id) {\n        return Operator.find(modelClass, id);\n    }\n\n    /**\n     * Basically same as {@link #find(Class, long)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param id\n     *            Which record to query.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> findAsync(Class<T> modelClass, long id) {\n        return Operator.findAsync(modelClass, id);\n    }\n\n    /**\n     * It is mostly same as {@link LitePal#find(Class, long)} but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param id\n     *            Which record to query.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return An object with found data from database, or null.\n     */\n    public static <T> T find(Class<T> modelClass, long id, boolean isEager) {\n        return Operator.find(modelClass, id, isEager);\n    }\n\n    /**\n     * Basically same as {@link #find(Class, long, boolean)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param id\n     *            Which record to query.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> findAsync(final Class<T> modelClass, final long id, final boolean isEager) {\n        return Operator.findAsync(modelClass, id, isEager);\n    }\n\n    /**\n     * Finds the first record of a single table.\n     *\n     * <pre>\n     * Person p = LitePal.findFirst(Person.class);\n     * </pre>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link LitePal#findFirst(Class, boolean)}.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @return An object with data of first row, or null.\n     */\n    public static <T> T findFirst(Class<T> modelClass) {\n        return Operator.findFirst(modelClass);\n    }\n\n    /**\n     * Basically same as {@link #findFirst(Class)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> findFirstAsync(Class<T> modelClass) {\n        return Operator.findFirstAsync(modelClass);\n    }\n\n    /**\n     * It is mostly same as {@link LitePal#findFirst(Class)} but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return An object with data of first row, or null.\n     */\n    public static <T> T findFirst(Class<T> modelClass, boolean isEager) {\n        return Operator.findFirst(modelClass, isEager);\n    }\n\n    /**\n     * Basically same as {@link #findFirst(Class, boolean)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> findFirstAsync(final Class<T> modelClass, final boolean isEager) {\n        return Operator.findFirstAsync(modelClass, isEager);\n    }\n\n    /**\n     * Finds the last record of a single table.\n     *\n     * <pre>\n     * Person p = LitePal.findLast(Person.class);\n     * </pre>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link LitePal#findLast(Class, boolean)}.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @return An object with data of last row, or null.\n     */\n    public static <T> T findLast(Class<T> modelClass) {\n        return Operator.findLast(modelClass);\n    }\n\n    /**\n     * Basically same as {@link #findLast(Class)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> findLastAsync(Class<T> modelClass) {\n        return Operator.findLastAsync(modelClass);\n    }\n\n    /**\n     * It is mostly same as {@link LitePal#findLast(Class)} but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return An object with data of last row, or null.\n     */\n    public static <T> T findLast(Class<T> modelClass, boolean isEager) {\n        return Operator.findLast(modelClass, isEager);\n    }\n\n    /**\n     * Basically same as {@link #findLast(Class, boolean)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    public static <T> FindExecutor<T> findLastAsync(final Class<T> modelClass, final boolean isEager) {\n        return Operator.findLastAsync(modelClass, isEager);\n    }\n\n    /**\n     * Finds multiple records by an id array.\n     *\n     * <pre>\n     * List&lt;Person&gt; people = LitePal.findAll(Person.class, 1, 2, 3);\n     *\n     * long[] bookIds = { 10, 18 };\n     * List&lt;Book&gt; books = LitePal.findAll(Book.class, bookIds);\n     * </pre>\n     *\n     * Of course you can find all records by passing nothing to the ids\n     * parameter.\n     *\n     * <pre>\n     * List&lt;Book&gt; allBooks = LitePal.findAll(Book.class);\n     * </pre>\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * {@link LitePal#findAll(Class, boolean, long...)}.\n     *\n     * The modelClass determines which table to query and the object type to\n     * return.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return as a list.\n     * @param ids\n     *            Which records to query. Or do not pass it to find all records.\n     * @return An object list with found data from database, or an empty list.\n     */\n    public static <T> List<T> findAll(Class<T> modelClass, long... ids) {\n        return Operator.findAll(modelClass, ids);\n    }\n\n    /**\n     * Basically same as {@link #findAll(Class, long...)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return as a list.\n     * @param ids\n     *            Which records to query. Or do not pass it to find all records.\n     * @return A FindMultiExecutor instance.\n     */\n    public static <T> FindMultiExecutor<T> findAllAsync(Class<T> modelClass, long... ids) {\n        return Operator.findAllAsync(modelClass, ids);\n    }\n\n    /**\n     * It is mostly same as {@link LitePal#findAll(Class, long...)} but an\n     * isEager parameter. If set true the associated models will be loaded as well.\n     * <br>\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return as a list.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @param ids\n     *            Which records to query. Or do not pass it to find all records.\n     * @return An object list with found data from database, or an empty list.\n     */\n    public static <T> List<T> findAll(Class<T> modelClass, boolean isEager, long... ids) {\n        return Operator.findAll(modelClass, isEager, ids);\n    }\n\n    /**\n     * Basically same as {@link #findAll(Class, boolean, long...)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to query and the object type to return as a list.\n     * @param isEager\n     *            True to load the associated models, false not.\n     * @param ids\n     *            Which records to query. Or do not pass it to find all records.\n     * @return A FindMultiExecutor instance.\n     */\n    public static <T> FindMultiExecutor<T> findAllAsync(final Class<T> modelClass, final boolean isEager, final long... ids) {\n        return Operator.findAllAsync(modelClass, isEager, ids);\n    }\n\n    /**\n     * Runs the provided SQL and returns a Cursor over the result set. You may\n     * include ? in where clause in the query, which will be replaced by the\n     * second to the last parameters, such as:\n     *\n     * <pre>\n     * Cursor cursor = LitePal.findBySQL(&quot;select * from person where name=? and age=?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     * </pre>\n     *\n     * @param sql\n     *            First parameter is the SQL clause to apply. Second to the last\n     *            parameters will replace the place holders.\n     * @return A Cursor object, which is positioned before the first entry. Note\n     *         that Cursors are not synchronized, see the documentation for more\n     *         details.\n     */\n    public static Cursor findBySQL(String... sql) {\n        return Operator.findBySQL(sql);\n    }\n\n    /**\n     * Deletes the record in the database by id.<br>\n     * The data in other tables which is referenced with the record will be\n     * removed too.\n     *\n     * <pre>\n     * LitePal.delete(Person.class, 1);\n     * </pre>\n     *\n     * This means that the record 1 in person table will be removed.\n     *\n     * @param modelClass\n     *            Which table to delete from by class.\n     * @param id\n     *            Which record to delete.\n     * @return The number of rows affected. Including cascade delete rows.\n     */\n    public static int delete(Class<?> modelClass, long id) {\n        return Operator.delete(modelClass, id);\n    }\n\n    /**\n     * Basically same as {@link #delete(Class, long)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to delete from by class.\n     * @param id\n     *            Which record to delete.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    public static UpdateOrDeleteExecutor deleteAsync(final Class<?> modelClass, final long id) {\n        return Operator.deleteAsync(modelClass, id);\n    }\n\n    /**\n     * Deletes all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL DELETE statement and sends\n     * it to the database.\n     *\n     * <pre>\n     * LitePal.deleteAll(Person.class, &quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     * </pre>\n     *\n     * This means that all the records which name is Tom and age is 14 will be\n     * removed.<br>\n     *\n     * @param modelClass\n     *            Which table to delete from by class.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            deleting. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return The number of rows affected.\n     */\n    public static int deleteAll(Class<?> modelClass, String... conditions) {\n        return Operator.deleteAll(modelClass, conditions);\n    }\n\n    /**\n     * Basically same as {@link #deleteAll(Class, String...)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to delete from by class.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            deleting. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    public static UpdateOrDeleteExecutor deleteAllAsync(final Class<?> modelClass, final String... conditions) {\n        return Operator.deleteAllAsync(modelClass, conditions);\n    }\n\n    /**\n     * Deletes all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL DELETE statement and sends\n     * it to the database.\n     *\n     * <pre>\n     * LitePal.deleteAll(&quot;person&quot;, &quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     * </pre>\n     *\n     * This means that all the records which name is Tom and age is 14 will be\n     * removed.<br>\n     *\n     * Note that this method won't delete the referenced data in other tables.\n     * You should remove those values by your own.\n     *\n     * @param tableName\n     *            Which table to delete from.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            deleting. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return The number of rows affected.\n     */\n    public static int deleteAll(String tableName, String... conditions) {\n        return Operator.deleteAll(tableName, conditions);\n    }\n\n    /**\n     * Basically same as {@link #deleteAll(String, String...)} but pending to a new thread for executing.\n     *\n     * @param tableName\n     *            Which table to delete from.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            deleting. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    public static UpdateOrDeleteExecutor deleteAllAsync(final String tableName, final String... conditions) {\n        return Operator.deleteAllAsync(tableName, conditions);\n    }\n\n    /**\n     * Updates the corresponding record by id with ContentValues. Returns the\n     * number of affected rows.\n     *\n     * <pre>\n     * ContentValues cv = new ContentValues();\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     * LitePal.update(Person.class, cv, 1);\n     * </pre>\n     *\n     * This means that the name of record 1 will be updated into Jim.<br>\n     *\n     * @param modelClass\n     *            Which table to update by class.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param id\n     *            Which record to update.\n     * @return The number of rows affected.\n     */\n    public static int update(Class<?> modelClass, ContentValues values, long id) {\n        return Operator.update(modelClass, values, id);\n    }\n\n    /**\n     * Basically same as {@link #update(Class, ContentValues, long)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to update by class.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param id\n     *            Which record to update.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    public static UpdateOrDeleteExecutor updateAsync(final Class<?> modelClass, final ContentValues values, final long id) {\n        return Operator.updateAsync(modelClass, values, id);\n    }\n\n    /**\n     * Updates all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL UPDATE statement and sends\n     * it to the database.\n     *\n     * <pre>\n     * ContentValues cv = new ContentValues();\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     * LitePal.update(Person.class, cv, &quot;name = ?&quot;, &quot;Tom&quot;);\n     * </pre>\n     *\n     * This means that all the records which name is Tom will be updated into\n     * Jim.\n     *\n     * @param modelClass\n     *            Which table to update by class.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            updating. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return The number of rows affected.\n     */\n    public static int updateAll(Class<?> modelClass, ContentValues values, String... conditions) {\n        return Operator.updateAll(modelClass, values, conditions);\n    }\n\n    /**\n     * Basically same as {@link #updateAll(Class, ContentValues, String...)} but pending to a new thread for executing.\n     *\n     * @param modelClass\n     *            Which table to update by class.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            updating. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    public static UpdateOrDeleteExecutor updateAllAsync(Class<?> modelClass, ContentValues values, String... conditions) {\n        return Operator.updateAllAsync(modelClass, values, conditions);\n    }\n\n    /**\n     * Updates all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL UPDATE statement and sends\n     * it to the database.\n     *\n     * <pre>\n     * ContentValues cv = new ContentValues();\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     * LitePal.update(&quot;person&quot;, cv, &quot;name = ?&quot;, &quot;Tom&quot;);\n     * </pre>\n     *\n     * This means that all the records which name is Tom will be updated into\n     * Jim.\n     *\n     * @param tableName\n     *            Which table to update.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            updating. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return The number of rows affected.\n     */\n    public static int updateAll(String tableName, ContentValues values, String... conditions) {\n        return Operator.updateAll(tableName, values, conditions);\n    }\n\n    /**\n     * Basically same as {@link #updateAll(String, ContentValues, String...)} but pending to a new thread for executing.\n     *\n     * @param tableName\n     *            Which table to update.\n     * @param values\n     *            A map from column names to new column values. null is a valid\n     *            value that will be translated to NULL.\n     * @param conditions\n     *            A string array representing the WHERE part of an SQL\n     *            statement. First parameter is the WHERE clause to apply when\n     *            updating. The way of specifying place holders is to insert one\n     *            or more question marks in the SQL. The first question mark is\n     *            replaced by the second element of the array, the next question\n     *            mark by the third, and so on. Passing empty string will update\n     *            all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    public static UpdateOrDeleteExecutor updateAllAsync(final String tableName, final ContentValues values, final String... conditions) {\n        return Operator.updateAllAsync(tableName, values, conditions);\n    }\n\n    /**\n     * Saves the collection into database. <br>\n     *\n     * <pre>\n     * LitePal.saveAll(people);\n     * </pre>\n     *\n     * If the model in collection is a new record gets created in the database,\n     * otherwise the existing record gets updated.<br>\n     * If saving process failed by any accident, the whole action will be\n     * cancelled and your database will be <b>rolled back</b>. <br>\n     * This method acts the same result as the below way, but <b>much more\n     * efficient</b>.\n     *\n     * <pre>\n     * for (Person person : people) {\n     * \tperson.save();\n     * }\n     * </pre>\n     *\n     * So when your collection holds huge of models, saveAll(Collection) is the better choice.\n     *\n     * @param collection\n     *            Holds all models to save.\n     */\n    public static <T extends LitePalSupport> void saveAll(Collection<T> collection) {\n        Operator.saveAll(collection);\n    }\n\n    /**\n     * Basically same as {@link #saveAll(Collection)} but pending to a new thread for executing.\n     *\n     * @param collection\n     *            Holds all models to save.\n     * @return A SaveExecutor instance.\n     */\n    public static <T extends LitePalSupport> SaveExecutor saveAllAsync(final Collection<T> collection) {\n        return Operator.saveAllAsync(collection);\n    }\n\n    /**\n     * Provide a way to mark all models in collection as deleted. This means these models' save\n     * state is no longer exist anymore. If save them again, they will be treated as inserting new\n     * data instead of updating the exist one.\n     * @param collection\n     *          Collection of models which want to mark as deleted and clear their save state.\n     */\n    public static <T extends LitePalSupport> void markAsDeleted(Collection<T> collection) {\n        Operator.markAsDeleted(collection);\n    }\n\n    /**\n     * Check if the specified conditions data already exists in the table.\n     * @param modelClass\n     *          Which table to check by class.\n     * @param conditions\n     *          A filter declaring which data to check. Exactly same use as\n     *          {@link LitePal#where(String...)}, except null conditions will result in false.\n     * @return Return true if the specified conditions data already exists in the table.\n     *         False otherwise. Null conditions will result in false.\n     */\n    public static <T> boolean isExist(Class<T> modelClass, String... conditions) {\n        return Operator.isExist(modelClass, conditions);\n    }\n\n    /**\n     * Register a listener to listen database create and upgrade events.\n     */\n    public static void registerDatabaseListener(DatabaseListener listener) {\n        Operator.registerDatabaseListener(listener);\n    }\n\n}"
  },
  {
    "path": "java/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">LitePal</string>\n</resources>\n"
  },
  {
    "path": "kotlin/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "kotlin/bintray.gradle",
    "content": "group = PROJ_GROUP\nversion = PROJ_VERSION\nproject.archivesBaseName = PROJ_ARTIFACTID_KT\n\napply plugin: 'com.jfrog.bintray'\napply plugin: 'maven-publish'\n\ntask sourcesJar(type: Jar) {\n    from android.sourceSets.main.java.srcDirs\n    classifier = 'sources'\n}\n\nartifacts {\n    archives sourcesJar\n}\n\ndef pomConfig = {\n    licenses {\n        license {\n            name \"The Apache Software License, Version 2.0\"\n            url \"http://www.apache.org/licenses/LICENSE-2.0.txt\"\n            distribution \"repo\"\n        }\n    }\n    developers {\n        developer {\n            id DEVELOPER_ID\n            name DEVELOPER_NAME\n            email DEVELOPER_EMAIL\n        }\n    }\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            artifactId PROJ_ARTIFACTID_KT\n\n            pom{\n                packaging 'aar'\n            }\n//            pom.withXml {\n//                def root = asNode()\n//                root.appendNode('description', PROJ_DESCRIPTION)\n//                root.children().last() + pomConfig\n//            }\n            //The publication doesn't know about our dependencies, so we have to manually add them to the pom\n            pom.withXml {\n                def dependenciesNode = asNode().appendNode('dependencies')\n\n                //Iterate over the compile dependencies (we don't want the test ones), adding a <dependency> node for each\n                configurations.api.allDependencies.each {\n                    def dependencyNode = dependenciesNode.appendNode('dependency')\n                    dependencyNode.appendNode('groupId', it.group)\n                    dependencyNode.appendNode('artifactId', it.name)\n                    dependencyNode.appendNode('version', it.version)\n                }\n            }\n        }\n    }\n}\n\nbintray {\n    user = BINTRAY_USER\n    key = BINTRAY_KEY\n\n    configurations = ['archives']\n    publications = ['mavenJava']\n    publish = true\n\n    pkg {\n        repo = 'maven'\n        name = PROJ_NAME_KT\n        desc = PROJ_DESCRIPTION\n        websiteUrl = PROJ_WEBSITEURL\n        vcsUrl = PROJ_VCSURL\n        licenses = ['Apache-2.0']\n        publicDownloadNumbers = true\n    }\n}"
  },
  {
    "path": "kotlin/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugin: 'org.jetbrains.dokka-android'\n\nandroid {\n    compileSdkVersion 28\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 28\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n    api project(':core')\n}\n\nif (hasProperty(\"BINTRAY_KEY\")) {\n    apply from: 'bintray.gradle'\n}"
  },
  {
    "path": "kotlin/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /home/tony/Android/Sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "kotlin/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"org.litepal.kotlin\">\n\n    <application>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "kotlin/src/main/java/org/litepal/LitePal.kt",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal\n\nimport android.content.ContentValues\nimport android.content.Context\nimport android.database.Cursor\nimport android.database.sqlite.SQLiteDatabase\nimport android.os.Handler\nimport android.os.Looper\nimport android.text.TextUtils\nimport org.litepal.crud.*\nimport org.litepal.crud.async.*\nimport org.litepal.exceptions.LitePalSupportException\nimport org.litepal.parser.LitePalAttr\nimport org.litepal.parser.LitePalParser\nimport org.litepal.tablemanager.Connector\nimport org.litepal.tablemanager.callback.DatabaseListener\nimport org.litepal.util.BaseUtility\nimport org.litepal.util.Const\nimport org.litepal.util.DBUtility\nimport org.litepal.util.SharedUtil\nimport org.litepal.util.cipher.CipherUtil\nimport java.io.File\nimport kotlin.math.tan\n\n/**\n * LitePal is an Android library that allows developers to use SQLite database extremely easy.\n * You can initialized it by calling {@link #initialize(Context)} method to make LitePal ready to\n * work. Also you can switch the using database by calling {@link #use(LitePalDB)} and {@link #useDefault()}\n * methods.\n *\n * @author Tony Green\n * @since 2.0\n */\nobject LitePal {\n\n    /**\n     * Initialize to make LitePal ready to work. If you didn't configure LitePalApplication\n     * in the AndroidManifest.xml, make sure you call this method as soon as possible. In\n     * Application's onCreate() method will be fine.\n     *\n     * @param context\n     * Application context.\n     */\n    @JvmStatic\n    fun initialize(context: Context) {\n        Operator.initialize(context)\n    }\n\n    /**\n     * Get a writable SQLiteDatabase.\n     *\n     * @return A writable SQLiteDatabase instance\n     */\n    @JvmStatic\n    fun getDatabase(): SQLiteDatabase = Operator.getDatabase()\n\n    /**\n     * Switch the using database to the one specified by parameter.\n     * @param litePalDB\n     * The database to switch to.\n     */\n    @JvmStatic\n    fun use(litePalDB: LitePalDB) {\n        Operator.use(litePalDB)\n    }\n\n\n    /**\n     * Switch the using database to default with configuration by litepal.xml.\n     */\n    @JvmStatic\n    fun useDefault() {\n        Operator.useDefault()\n    }\n\n    /**\n     * Delete the specified database.\n     * @param dbName\n     * Name of database to delete.\n     * @return True if delete success, false otherwise.\n     */\n    @JvmStatic\n    fun deleteDatabase(dbName: String) = Operator.deleteDatabase(dbName)\n\n    @JvmStatic\n    fun aesKey(key: String) {\n        Operator.aesKey(key)\n    }\n\n    /**\n     * Declaring to query which columns in table.\n     *\n     * LitePal.select(&quot;name&quot;, &quot;age&quot;).find(Person.class);\n     *\n     * This will find all rows with name and age columns in Person table.\n     *\n     * @param columns\n     * A String array of which columns to return. Passing null will\n     * return all columns.\n     *\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun select(vararg columns: String?) = Operator.select(*columns)\n\n    /**\n     * Declaring to query which rows in table.\n     *\n     * LitePal.where(&quot;name = ? or age &gt; ?&quot;, &quot;Tom&quot;, &quot;14&quot;).find(Person.class);\n     *\n     * This will find rows which name is Tom or age greater than 14 in Person\n     * table.\n     *\n     * @param conditions\n     * A filter declaring which rows to return, formatted as an SQL\n     * WHERE clause. Passing null will return all rows.\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun where(vararg conditions: String?) = Operator.where(*conditions)\n\n    /**\n     * Declaring how to order the rows queried from table.\n     *\n     * LitePal.order(&quot;name desc&quot;).find(Person.class);\n     *\n     * This will find all rows in Person table sorted by name with inverted\n     * order.\n     *\n     * @param column\n     * How to order the rows, formatted as an SQL ORDER BY clause.\n     * Passing null will use the default sort order, which may be\n     * unordered.\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun order(column: String?) = Operator.order(column)\n\n    /**\n     * Limits the number of rows returned by the query.\n     *\n     * LitePal.limit(2).find(Person.class);\n     *\n     * This will find the top 2 rows in Person table.\n     *\n     * @param value\n     * Limits the number of rows returned by the query, formatted as\n     * LIMIT clause.\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun limit(value: Int) = Operator.limit(value)\n\n    /**\n     * Declaring the offset of rows returned by the query. This method must be\n     * used with [LitePal.limit], or nothing will return.\n     *\n     * LitePal.limit(1).offset(2).find(Person.class);\n     *\n     * This will find the third row in Person table.\n     *\n     * @param value\n     * The offset amount of rows returned by the query.\n     * @return A FluentQuery instance.\n     */\n    @JvmStatic\n    fun offset(value: Int) = Operator.offset(value)\n\n    /**\n     * Count the records.\n     *\n     * LitePal.count(Person.class);\n     *\n     * This will count all rows in person table.\n     *\n     * You can also specify a where clause when counting.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(Person.class);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @return Count of the specified table.\n     */\n    @JvmStatic\n    fun count(modelClass: Class<*>) = Operator.count(modelClass)\n\n    /**\n     * Basically same as [LitePal.count] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @return A CountExecutor instance.\n     */\n    @JvmStatic\n    fun countAsync(modelClass: Class<*>) = Operator.countAsync(modelClass)\n\n    /**\n     * Count the records.\n     *\n     * LitePal.count(&quot;person&quot;);\n     *\n     * This will count all rows in person table.\n     *\n     * You can also specify a where clause when counting.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(&quot;person&quot;);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @return Count of the specified table.\n     */\n    @JvmStatic\n    fun count(tableName: String) = Operator.count(tableName)\n\n    /**\n     * Basically same as [LitePal.count] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @return A CountExecutor instance.\n     */\n    @JvmStatic\n    fun countAsync(tableName: String) = Operator.countAsync(tableName)\n\n    /**\n     * Calculates the average value on a given column.\n     *\n     * LitePal.average(Person.class, &quot;age&quot;);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(Person.class, &quot;age&quot;);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param column\n     * The based on column to calculate.\n     * @return The average value on a given column.\n     */\n    @JvmStatic\n    fun average(modelClass: Class<*>, column: String) = Operator.average(modelClass, column)\n\n    /**\n     * Basically same as [LitePal.average] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param column\n     * The based on column to calculate.\n     * @return A AverageExecutor instance.\n     */\n    @JvmStatic\n    fun averageAsync(modelClass: Class<*>, column: String) = Operator.averageAsync(modelClass, column)\n\n    /**\n     * Calculates the average value on a given column.\n     *\n     * LitePal.average(&quot;person&quot;, &quot;age&quot;);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(&quot;person&quot;, &quot;age&quot;);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param column\n     * The based on column to calculate.\n     * @return The average value on a given column.\n     */\n    @JvmStatic\n    fun average(tableName: String, column: String) = Operator.average(tableName, column)\n\n    /**\n     * Basically same as [LitePal.average] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param column\n     * The based on column to calculate.\n     * @return A AverageExecutor instance.\n     */\n    @JvmStatic\n    fun averageAsync(tableName: String, column: String) = Operator.averageAsync(tableName, column)\n\n    /**\n     * Calculates the maximum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.max(Person.class, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(Person.class, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The maximum value on a given column.\n     */\n    @JvmStatic\n    fun <T> max(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.max(modelClass, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.max] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> maxAsync(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.maxAsync(modelClass, columnName, columnType)\n\n    /**\n     * Calculates the maximum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.max(&quot;person&quot;, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The maximum value on a given column.\n     */\n    @JvmStatic\n    fun <T> max(tableName: String, columnName: String, columnType: Class<T>) = Operator.max(tableName, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.max] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> maxAsync(tableName: String, columnName: String, columnType: Class<T>) = Operator.maxAsync(tableName, columnName, columnType)\n\n    /**\n     * Calculates the minimum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.min(Person.class, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(Person.class, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The minimum value on a given column.\n     */\n    @JvmStatic\n    fun <T> min(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.min(modelClass, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.min] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> minAsync(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.minAsync(modelClass, columnName, columnType)\n\n    /**\n     * Calculates the minimum value on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.min(&quot;person&quot;, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The minimum value on a given column.\n     */\n    @JvmStatic\n    fun <T> min(tableName: String, columnName: String, columnType: Class<T>) = Operator.min(tableName, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.min] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> minAsync(tableName: String, columnName: String, columnType: Class<T>) = Operator.minAsync(tableName, columnName, columnType)\n\n    /**\n     * Calculates the sum of values on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.sum(Person.class, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(Person.class, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The sum value on a given column.\n     */\n    @JvmStatic\n    fun <T> sum(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.sum(modelClass, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.sum] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query from by class.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> sumAsync(modelClass: Class<*>, columnName: String, columnType: Class<T>) = Operator.sumAsync(modelClass, columnName, columnType)\n\n    /**\n     * Calculates the sum of values on a given column. The value is returned\n     * with the same data type of the column.\n     *\n     * LitePal.sum(&quot;person&quot;, &quot;age&quot;, int.class);\n     *\n     * You can also specify a where clause when calculating.\n     *\n     * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return The sum value on a given column.\n     */\n    @JvmStatic\n    fun <T> sum(tableName: String, columnName: String, columnType: Class<T>) = Operator.sum(tableName, columnName, columnType)\n\n    /**\n     * Basically same as [LitePal.sum] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to query from.\n     * @param columnName\n     * The based on column to calculate.\n     * @param columnType\n     * The type of the based on column.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> sumAsync(tableName: String, columnName: String, columnType: Class<T>) = Operator.sumAsync(tableName, columnName, columnType)\n\n    /**\n     * Finds the record by a specific id.\n     *\n     * Person p = LitePal.find(Person.class, 1);\n     *\n     * The modelClass determines which table to query and the object type to\n     * return. If no record can be found, then return null.\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * [LitePal.find].\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param id\n     * Which record to query.\n     * @return An object with found data from database, or null.\n     */\n    @JvmStatic\n    fun <T> find(modelClass: Class<T>, id: Long) = Operator.find(modelClass, id)\n\n    /**\n     * Basically same as [LitePal.find] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param id\n     * Which record to query.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> findAsync(modelClass: Class<T>, id: Long) = Operator.findAsync(modelClass, id)\n\n    /**\n     * It is mostly same as [LitePal.find] but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     *\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param id\n     * Which record to query.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return An object with found data from database, or null.\n     */\n    @JvmStatic\n    fun <T> find(modelClass: Class<T>, id: Long, isEager: Boolean) = Operator.find(modelClass, id, isEager)\n\n    /**\n     * Basically same as [LitePal.find] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param id\n     * Which record to query.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> findAsync(modelClass: Class<T>, id: Long, isEager: Boolean) = Operator.findAsync(modelClass, id, isEager)\n\n    /**\n     * Finds the first record of a single table.\n     *\n     * Person p = LitePal.findFirst(Person.class);\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * [LitePal.findFirst].\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @return An object with data of first row, or null.\n     */\n    @JvmStatic\n    fun <T> findFirst(modelClass: Class<T>) = Operator.findFirst(modelClass)\n\n    /**\n     * Basically same as [LitePal.findFirst] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> findFirstAsync(modelClass: Class<T>) = Operator.findFirstAsync(modelClass)\n\n    /**\n     * It is mostly same as [LitePal.findFirst] but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     *\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return An object with data of first row, or null.\n     */\n    @JvmStatic\n    fun <T> findFirst(modelClass: Class<T>, isEager: Boolean) = Operator.findFirst(modelClass, isEager)\n\n    /**\n     * Basically same as [LitePal.findFirst] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> findFirstAsync(modelClass: Class<T>, isEager: Boolean) = Operator.findFirstAsync(modelClass, isEager)\n\n    /**\n     * Finds the last record of a single table.\n     *\n     * Person p = LitePal.findLast(Person.class);\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * [LitePal.findLast].\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @return An object with data of last row, or null.\n     */\n    @JvmStatic\n    fun <T> findLast(modelClass: Class<T>) = Operator.findLast(modelClass)\n\n    /**\n     * Basically same as [LitePal.findLast] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> findLastAsync(modelClass: Class<T>) = Operator.findLastAsync(modelClass)\n\n    /**\n     * It is mostly same as [LitePal.findLast] but an isEager\n     * parameter. If set true the associated models will be loaded as well.\n     *\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return An object with data of last row, or null.\n     */\n    @JvmStatic\n    fun <T> findLast(modelClass: Class<T>, isEager: Boolean) = Operator.findLast(modelClass, isEager)\n\n    /**\n     * Basically same as [LitePal.findLast] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @return A FindExecutor instance.\n     */\n    @JvmStatic\n    fun <T> findLastAsync(modelClass: Class<T>, isEager: Boolean) = Operator.findLastAsync(modelClass, isEager)\n\n    /**\n     * Finds multiple records by an id array.\n     *\n     * List&lt;Person&gt; people = LitePal.findAll(Person.class, 1, 2, 3);\n     *\n     * long[] bookIds = { 10, 18 };\n     * List&lt;Book&gt; books = LitePal.findAll(Book.class, bookIds);\n     *\n     * Of course you can find all records by passing nothing to the ids\n     * parameter.\n     *\n     * List&lt;Book&gt; allBooks = LitePal.findAll(Book.class);\n     *\n     * Note that the associated models won't be loaded by default considering\n     * the efficiency, but you can do that by using\n     * [LitePal.findAll].\n     *\n     * The modelClass determines which table to query and the object type to\n     * return.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return as a list.\n     * @param ids\n     * Which records to query. Or do not pass it to find all records.\n     * @return An object list with found data from database, or an empty list.\n     */\n    @JvmStatic\n    fun <T> findAll(modelClass: Class<T>, vararg ids: Long) = Operator.findAll(modelClass, *ids)\n\n    /**\n     * Basically same as [LitePal.findAll] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return as a list.\n     * @param ids\n     * Which records to query. Or do not pass it to find all records.\n     * @return A FindMultiExecutor instance.\n     */\n    @JvmStatic\n    fun <T> findAllAsync(modelClass: Class<T>, vararg ids: Long) = Operator.findAllAsync(modelClass, *ids)\n\n    /**\n     * It is mostly same as [LitePal.findAll] but an\n     * isEager parameter. If set true the associated models will be loaded as well.\n     *\n     * Note that isEager will only work for one deep level relation, considering the query efficiency.\n     * You have to implement on your own if you need to load multiple deepness of relation at once.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return as a list.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @param ids\n     * Which records to query. Or do not pass it to find all records.\n     * @return An object list with found data from database, or an empty list.\n     */\n    @JvmStatic\n    fun <T> findAll(modelClass: Class<T>, isEager: Boolean, vararg ids: Long) = Operator.findAll(modelClass, isEager, *ids)\n\n    /**\n     * Basically same as [LitePal.findAll] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to query and the object type to return as a list.\n     * @param isEager\n     * True to load the associated models, false not.\n     * @param ids\n     * Which records to query. Or do not pass it to find all records.\n     * @return A FindMultiExecutor instance.\n     */\n    @JvmStatic\n    fun <T> findAllAsync(modelClass: Class<T>, isEager: Boolean, vararg ids: Long) = Operator.findAllAsync(modelClass, isEager, *ids)\n\n    /**\n     * Runs the provided SQL and returns a Cursor over the result set. You may\n     * include ? in where clause in the query, which will be replaced by the\n     * second to the last parameters, such as:\n     *\n     * Cursor cursor = LitePal.findBySQL(&quot;select * from person where name=? and age=?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     *\n     * @param sql\n     * First parameter is the SQL clause to apply. Second to the last\n     * parameters will replace the place holders.\n     * @return A Cursor object, which is positioned before the first entry. Note\n     * that Cursors are not synchronized, see the documentation for more\n     * details.\n     */\n    @JvmStatic\n    fun findBySQL(vararg sql: String) = Operator.findBySQL(*sql)\n\n    /**\n     * Deletes the record in the database by id.\n     *\n     * The data in other tables which is referenced with the record will be\n     * removed too.\n     *\n     * LitePal.delete(Person.class, 1);\n     *\n     * This means that the record 1 in person table will be removed.\n     *\n     * @param modelClass\n     * Which table to delete from by class.\n     * @param id\n     * Which record to delete.\n     * @return The number of rows affected. Including cascade delete rows.\n     */\n    @JvmStatic\n    fun delete(modelClass: Class<*>, id: Long) = Operator.delete(modelClass, id)\n\n    /**\n     * Basically same as [LitePal.delete] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to delete from by class.\n     * @param id\n     * Which record to delete.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    fun deleteAsync(modelClass: Class<*>, id: Long) = Operator.deleteAsync(modelClass, id)\n\n    /**\n     * Deletes all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL DELETE statement and sends\n     * it to the database.\n     *\n     * LitePal.deleteAll(Person.class, &quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     *\n     * This means that all the records which name is Tom and age is 14 will be\n     * removed.\n     *\n     * @param modelClass\n     * Which table to delete from by class.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * deleting. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will delete\n     * all rows.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun deleteAll(modelClass: Class<*>, vararg conditions: String?) = Operator.deleteAll(modelClass, *conditions)\n\n    /**\n     * Basically same as [LitePal.deleteAll] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to delete from by class.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * deleting. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will delete\n     * all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    fun deleteAllAsync(modelClass: Class<*>, vararg conditions: String?) = Operator.deleteAllAsync(modelClass, *conditions)\n\n    /**\n     * Deletes all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL DELETE statement and sends\n     * it to the database.\n     *\n     * LitePal.deleteAll(&quot;person&quot;, &quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;);\n     *\n     * This means that all the records which name is Tom and age is 14 will be\n     * removed.\n     *\n     * Note that this method won't delete the referenced data in other tables.\n     * You should remove those values by your own.\n     *\n     * @param tableName\n     * Which table to delete from.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * deleting. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will delete\n     * all rows.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun deleteAll(tableName: String, vararg conditions: String?) = Operator.deleteAll(tableName, *conditions)\n\n    /**\n     * Basically same as [LitePal.deleteAll] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to delete from.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * deleting. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will delete\n     * all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    fun deleteAllAsync(tableName: String, vararg conditions: String?) = Operator.deleteAllAsync(tableName, *conditions)\n\n    /**\n     * Updates the corresponding record by id with ContentValues. Returns the\n     * number of affected rows.\n     *\n     * ContentValues cv = new ContentValues();\n     *\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     *\n     * LitePal.update(Person.class, cv, 1);\n     *\n     * This means that the name of record 1 will be updated into Jim.\n     *\n     * @param modelClass\n     * Which table to update by class.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param id\n     * Which record to update.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun update(modelClass: Class<*>, values: ContentValues, id: Long) = Operator.update(modelClass, values, id)\n\n    /**\n     * Basically same as [LitePal.update] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to update by class.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param id\n     * Which record to update.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    fun updateAsync(modelClass: Class<*>, values: ContentValues, id: Long) = Operator.updateAsync(modelClass, values, id)\n\n    /**\n     * Updates all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL UPDATE statement and sends\n     * it to the database.\n     *\n     * ContentValues cv = new ContentValues();\n     *\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     *\n     * LitePal.update(Person.class, cv, &quot;name = ?&quot;, &quot;Tom&quot;);\n     *\n     * This means that all the records which name is Tom will be updated into\n     * Jim.\n     *\n     * @param modelClass\n     * Which table to update by class.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * updating. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will update\n     * all rows.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun updateAll(modelClass: Class<*>, values: ContentValues, vararg conditions: String?) = Operator.updateAll(modelClass, values, *conditions)\n\n    /**\n     * Basically same as [LitePal.updateAll] but pending to a new thread for executing.\n     *\n     * @param modelClass\n     * Which table to update by class.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * updating. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will update\n     * all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    fun updateAllAsync(modelClass: Class<*>, values: ContentValues, vararg conditions: String?) = Operator.updateAllAsync(modelClass, values, *conditions)\n\n    /**\n     * Updates all records with details given if they match a set of conditions\n     * supplied. This method constructs a single SQL UPDATE statement and sends\n     * it to the database.\n     *\n     * ContentValues cv = new ContentValues();\n     *\n     * cv.put(&quot;name&quot;, &quot;Jim&quot;);\n     *\n     * LitePal.update(&quot;person&quot;, cv, &quot;name = ?&quot;, &quot;Tom&quot;);\n     *\n     * This means that all the records which name is Tom will be updated into\n     * Jim.\n     *\n     * @param tableName\n     * Which table to update.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * updating. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will update\n     * all rows.\n     * @return The number of rows affected.\n     */\n    @JvmStatic\n    fun updateAll(tableName: String, values: ContentValues, vararg conditions: String?) = Operator.updateAll(tableName, values, *conditions)\n\n    /**\n     * Basically same as [LitePal.updateAll] but pending to a new thread for executing.\n     *\n     * @param tableName\n     * Which table to update.\n     * @param values\n     * A map from column names to new column values. null is a valid\n     * value that will be translated to NULL.\n     * @param conditions\n     * A string array representing the WHERE part of an SQL\n     * statement. First parameter is the WHERE clause to apply when\n     * updating. The way of specifying place holders is to insert one\n     * or more question marks in the SQL. The first question mark is\n     * replaced by the second element of the array, the next question\n     * mark by the third, and so on. Passing empty string will update\n     * all rows.\n     * @return A UpdateOrDeleteExecutor instance.\n     */\n    @JvmStatic\n    fun updateAllAsync(tableName: String, values: ContentValues, vararg conditions: String?) = Operator.updateAllAsync(tableName, values, *conditions)\n\n    /**\n     * Saves the collection into database.\n     *\n     * LitePal.saveAll(people);\n     *\n     * If the model in collection is a new record gets created in the database,\n     * otherwise the existing record gets updated.\n     *\n     * If saving process failed by any accident, the whole action will be\n     * cancelled and your database will be **rolled back**.\n     *\n     * This method acts the same result as the below way, but **much more\n     * efficient**.\n     *\n     * for (Person person : people) {\n     *      person.save();\n     * }\n     *\n     * So when your collection holds huge of models, saveAll(Collection) is the better choice.\n     *\n     * @param collection\n     * Holds all models to save.\n     */\n    @JvmStatic\n    fun <T : LitePalSupport> saveAll(collection: Collection<T>) = Operator.saveAll(collection)\n\n    /**\n     * Basically same as [LitePal.saveAll] but pending to a new thread for executing.\n     *\n     * @param collection\n     * Holds all models to save.\n     * @return A SaveExecutor instance.\n     */\n    @JvmStatic\n    fun <T : LitePalSupport> saveAllAsync(collection: Collection<T>) = Operator.saveAllAsync(collection)\n\n    /**\n     * Provide a way to mark all models in collection as deleted. This means these models' save\n     * state is no longer exist anymore. If save them again, they will be treated as inserting new\n     * data instead of updating the exist one.\n     * @param collection\n     * Collection of models which want to mark as deleted and clear their save state.\n     */\n    @JvmStatic\n    fun <T : LitePalSupport> markAsDeleted(collection: Collection<T>) {\n        Operator.markAsDeleted(collection)\n    }\n\n    /**\n     * Check if the specified conditions data already exists in the table.\n     * @param modelClass\n     * Which table to check by class.\n     * @param conditions\n     * A filter declaring which data to check. Exactly same use as\n     * [LitePal.where], except null conditions will result in false.\n     * @return Return true if the specified conditions data already exists in the table.\n     * False otherwise. Null conditions will result in false.\n     */\n    @JvmStatic\n    fun <T> isExist(modelClass: Class<T>, vararg conditions: String?) = Operator.isExist(modelClass, *conditions)\n\n    /**\n     * Register a listener to listen database create and upgrade events.\n     */\n    @JvmStatic\n    fun registerDatabaseListener(listener: DatabaseListener) {\n        Operator.registerDatabaseListener(listener)\n    }\n\n}"
  },
  {
    "path": "kotlin/src/main/java/org/litepal/extension/FluentQuery.kt",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.extension\n\nimport org.litepal.FluentQuery\nimport org.litepal.crud.async.FindExecutor\n\n/**\n * Extension of FluentQuery class for Kotlin api.\n * @author Tony Green\n * @since 2.1\n */\n\n/**\n * Finds multiple records by the cluster parameters. You can use the below\n * way to finish a complicated query:\n *\n * LitePal.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(1).offset(2).find&lt;Person&gt;()\n *\n * You can also do the same job with SQLiteDatabase like this:\n *\n * getSQLiteDatabase().query(&quot;Person&quot;, &quot;name&quot;, &quot;age &gt; ?&quot;, new String[] { &quot;14&quot; }, null, null, &quot;age&quot;,\n * \t\t&quot;2,1&quot;)\n *\n * Obviously, the first way is much more semantic.<br>\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * {@link FluentQuery#find(Class, boolean)}.\n *\n * @return An object list with founded data from database, or an empty list.\n */\ninline fun <reified T> FluentQuery.find(): List<T> = find(T::class.java)\n\n/**\n * Basically same as {@link #find(Class)} but pending to a new thread for executing.\n *\n * @return A FindMultiExecutor instance.\n */\ninline fun <reified T> FluentQuery.findAsync() = findAsync(T::class.java)\n\n/**\n * It is mostly same as {@link FluentQuery#find(Class)} but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n *            True to load the associated models, false not.\n * @return An object list with founded data from database, or an empty list.\n */\ninline fun <reified T> FluentQuery.find(isEager: Boolean): List<T> = find(T::class.java, isEager)\n\n/**\n * Basically same as {@link #find(Class, boolean)} but pending to a new thread for executing.\n *\n * @param isEager\n *            True to load the associated models, false not.\n * @return A FindMultiExecutor instance.\n */\ninline fun <reified T> FluentQuery.findAsync(isEager: Boolean) = findAsync(T::class.java, isEager)\n\n/**\n * Finds the first record by the cluster parameters. You can use the below\n * way to finish a complicated query:\n *\n * LitePal.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(10).offset(2).findFirst&lt;Person&gt;()\n *\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * {@link FluentQuery#findFirst(Class, boolean)}.\n *\n * @return An object with founded data from database, or null.\n */\ninline fun <reified T> FluentQuery.findFirst(): T? = findFirst(T::class.java)\n\n/**\n * Basically same as {@link #findFirst(Class)} but pending to a new thread for executing.\n *\n * @return A FindExecutor instance.\n */\ninline fun <reified T> FluentQuery.findFirstAsync(): FindExecutor<T> = findFirstAsync(T::class.java)\n\n/**\n * It is mostly same as {@link FluentQuery#findFirst(Class)} but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n *            True to load the associated models, false not.\n * @return An object with founded data from database, or null.\n */\ninline fun <reified T> FluentQuery.findFirst(isEager: Boolean): T? = findFirst(T::class.java, isEager)\n\n/**\n * Finds the last record by the cluster parameters. You can use the below\n * way to finish a complicated query:\n *\n * LitePal.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(10).offset(2).findLast&lt;Person&gt;()\n *\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * {@link FluentQuery#findLast(Class, boolean)}.\n *\n * @return An object with founded data from database, or null.\n */\ninline fun <reified T> FluentQuery.findLast(): T? = findLast(T::class.java)\n\n/**\n * It is mostly same as {@link FluentQuery#findLast(Class)} but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n *            True to load the associated models, false not.\n * @return An object with founded data from database, or null.\n */\ninline fun <reified T> FluentQuery.findLast(isEager: Boolean): T? = findLast(T::class.java, isEager)\n\n/**\n * Count the records.\n *\n * LitePal.count&lt;Person&gt;()\n *\n * This will count all rows in person table.\n *\n * You can also specify a where clause when counting.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count&lt;Person&gt;()\n *\n * @return Count of the specified table.\n */\ninline fun <reified T> FluentQuery.count() = count(T::class.java)\n\n/**\n * Calculates the average value on a given column.\n *\n * LitePal.average&lt;Person&gt;(&quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average&lt;Person&gt;(&quot;age&quot;)\n *\n * @param column\n * The based on column to calculate.\n * @return The average value on a given column.\n */\ninline fun <reified T> FluentQuery.average(column: String) = average(T::class.java, column)\n\n/**\n * Calculates the maximum value on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.max&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * @param columnName\n * The based on column to calculate.\n *\n * @return The maximum value on a given column.\n */\ninline fun <reified T, reified R> FluentQuery.max(columnName: String): R = max(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the maximum value on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.max&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The maximum value on a given column.\n */\ninline fun <reified R> FluentQuery.max(tableName: String, columnName: String): R = max(tableName, columnName, R::class.java)\n\n/**\n * Calculates the minimum value on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.min&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * @param columnName\n * The based on column to calculate.\n * @return The minimum value on a given column.\n */\ninline fun <reified T, reified R> FluentQuery.min(columnName: String): R = min(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the minimum value on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.min&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The minimum value on a given column.\n */\ninline fun <reified R> FluentQuery.min(tableName: String, columnName: String): R = min(tableName, columnName, R::class.java)\n\n/**\n * Calculates the sum of values on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.sum&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * @param columnName\n * The based on column to calculate.\n * @return The sum value on a given column.\n */\ninline fun <reified T, reified R> FluentQuery.sum(columnName: String): R = sum(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the sum of values on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.sum&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The sum value on a given column.\n */\ninline fun <reified R> FluentQuery.sum(tableName: String, columnName: String): R = sum(tableName, columnName, R::class.java)"
  },
  {
    "path": "kotlin/src/main/java/org/litepal/extension/LitePal.kt",
    "content": "/*\n * Copyright (C)  Tony Green, LitePal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.extension\n\nimport android.content.ContentValues\nimport org.litepal.LitePal\n\n/**\n * Extension of LitePal class for Kotlin api.\n * @author Tony Green\n * @since 2.1\n */\n\n/**\n * Count the records.\n *\n * LitePal.count&lt;Person&gt;()\n *\n * This will count all rows in person table.\n *\n * You can also specify a where clause when counting.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count&lt;Person&gt;()\n *\n * @return Count of the specified table.\n */\ninline fun <reified T> LitePal.count() = count(T::class.java)\n\n/**\n * Basically same as [LitePal.count] but pending to a new thread for executing.\n *\n * @return A CountExecutor instance.\n */\ninline fun <reified T> LitePal.countAsync() = countAsync(T::class.java)\n\n/**\n * Calculates the average value on a given column.\n *\n * LitePal.average&lt;Person&gt;(&quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average&lt;Person&gt;(&quot;age&quot;)\n *\n * @param column\n * The based on column to calculate.\n * @return The average value on a given column.\n */\ninline fun <reified T> LitePal.average(column: String) = average(T::class.java, column)\n\n/**\n * Basically same as [LitePal.average] but pending to a new thread for executing.\n *\n * @param column\n * The based on column to calculate.\n * @return A AverageExecutor instance.\n */\ninline fun <reified T> LitePal.averageAsync(column: String) = averageAsync(T::class.java, column)\n\n/**\n * Calculates the maximum value on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.max&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * @param columnName\n * The based on column to calculate.\n *\n * @return The maximum value on a given column.\n */\ninline fun <reified T, reified R> LitePal.max(columnName: String) = max(T::class.java, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.max] but pending to a new thread for executing.\n *\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\ninline fun <reified T, reified R> LitePal.maxAsync(columnName: String) = maxAsync(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the maximum value on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.max&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The maximum value on a given column.\n */\ninline fun <reified R> LitePal.max(tableName: String, columnName: String) = max(tableName, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.max] but pending to a new thread for executing.\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\ninline fun <reified R> LitePal.maxAsync(tableName: String, columnName: String) = maxAsync(tableName, columnName, R::class.java)\n\n/**\n * Calculates the minimum value on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.min&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * @param columnName\n * The based on column to calculate.\n * @return The minimum value on a given column.\n */\ninline fun <reified T, reified R> LitePal.min(columnName: String) = min(T::class.java, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.min] but pending to a new thread for executing.\n *\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\ninline fun <reified T, reified R> LitePal.minAsync(columnName: String) = minAsync(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the minimum value on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.min&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The minimum value on a given column.\n */\ninline fun <reified R> LitePal.min(tableName: String, columnName: String) = min(tableName, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.min] but pending to a new thread for executing.\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\ninline fun <reified R> LitePal.minAsync(tableName: String, columnName: String) = minAsync(tableName, columnName, R::class.java)\n\n/**\n * Calculates the sum of values on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.sum&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum&lt;Person, Int&gt;(&quot;age&quot;)\n *\n * @param columnName\n * The based on column to calculate.\n * @return The sum value on a given column.\n */\ninline fun <reified T, reified R> LitePal.sum(columnName: String) = sum(T::class.java, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.sum] but pending to a new thread for executing.\n *\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\ninline fun <reified T, reified R> LitePal.sumAsync(columnName: String) = sumAsync(T::class.java, columnName, R::class.java)\n\n/**\n * Calculates the sum of values on a given column. The value is returned\n * with the same data type of the column.\n *\n * LitePal.sum&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * You can also specify a where clause when calculating.\n *\n * LitePal.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum&lt;Int&gt;(&quot;person&quot;, &quot;age&quot;)\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return The sum value on a given column.\n */\ninline fun <reified R> LitePal.sum(tableName: String, columnName: String) = sum(tableName, columnName, R::class.java)\n\n/**\n * Basically same as [LitePal.sum] but pending to a new thread for executing.\n *\n * @param tableName\n * Which table to query from.\n * @param columnName\n * The based on column to calculate.\n * @return A FindExecutor instance.\n */\ninline fun <reified R> LitePal.sumAsync(tableName: String, columnName: String) = sumAsync(tableName, columnName, R::class.java)\n\n/**\n * Finds the record by a specific id.\n *\n * val person = LitePal.find&lt;Person&gt;(1)\n *\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using[LitePal.find] with isEager parameter.\n *\n * @param id\n * Which record to query.\n * @return An object with found data from database, or null.\n */\ninline fun <reified T> LitePal.find(id: Long): T? = find(T::class.java, id)\n\n/**\n * Basically same as [LitePal.find] but pending to a new thread for executing.\n *\n * @param id\n * Which record to query.\n * @return A FindExecutor instance.\n */\ninline fun <reified T> LitePal.findAsync(id: Long) = findAsync(T::class.java, id)\n\n/**\n * It is mostly same as [LitePal.find] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param id\n * Which record to query.\n * @param isEager\n * True to load the associated models, false not.\n * @return An object with found data from database, or null.\n */\ninline fun <reified T> LitePal.find(id: Long, isEager: Boolean) = find(T::class.java, id, isEager)\n\n/**\n * Basically same as [LitePal.find] but pending to a new thread for executing.\n *\n * @param id\n * Which record to query.\n * @param isEager\n * True to load the associated models, false not.\n * @return A FindExecutor instance.\n */\ninline fun <reified T> LitePal.findAsync(id: Long, isEager: Boolean) = find(T::class.java, id, isEager)\n\n/**\n * Finds the first record of a single table.\n *\n * val person = LitePal.findFirst&lt;Person&gt;()\n *\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * [LitePal.findFirst] with isEager parameter.\n *\n * @return An object with data of first row, or null.\n */\ninline fun <reified T> LitePal.findFirst() = findFirst(T::class.java)\n\n/**\n * Basically same as [LitePal.findFirst] but pending to a new thread for executing.\n *\n * @return A FindExecutor instance.\n */\ninline fun <reified T> LitePal.findFirstAsync() = findFirstAsync(T::class.java)\n\n/**\n * It is mostly same as [LitePal.findFirst] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @return An object with data of first row, or null.\n */\ninline fun <reified T> LitePal.findFirst(isEager: Boolean) = findFirst(T::class.java, isEager)\n\n/**\n * Basically same as [LitePal.findFirst] but pending to a new thread for executing.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @return A FindExecutor instance.\n */\ninline fun <reified T> LitePal.findFirstAsync(isEager: Boolean) = findFirstAsync(T::class.java, isEager)\n\n/**\n * Finds the last record of a single table.\n *\n * val p = LitePal.findLast&lt;Person&gt;()\n *\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * [LitePal.findLast] with isEager parameter.\n *\n * @return An object with data of last row, or null.\n */\ninline fun <reified T> LitePal.findLast() = findLast(T::class.java)\n\n/**\n * Basically same as [LitePal.findLast] but pending to a new thread for executing.\n *\n * @return A FindExecutor instance.\n */\ninline fun <reified T> LitePal.findLastAsync() = findLastAsync(T::class.java)\n\n/**\n * It is mostly same as [LitePal.findLast] but an isEager\n * parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @return An object with data of last row, or null.\n */\ninline fun <reified T> LitePal.findLast(isEager: Boolean) = findLast(T::class.java, isEager)\n\n/**\n * Basically same as [LitePal.findLast] but pending to a new thread for executing.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @return A FindExecutor instance.\n */\ninline fun <reified T> LitePal.findLastAsync(isEager: Boolean) = findLastAsync(T::class.java, isEager)\n\n/**\n * Finds multiple records by an id array.\n *\n * val people = LitePal.findAll&lt;Person&gt;(1, 2, 3)\n *\n * val bookIds = longArrayOf(10, 18)\n *\n * LitePal.findAll&lt;Book&gt;(*bookIds)\n *\n * Of course you can find all records by passing nothing to the ids\n * parameter.\n *\n * val allBooks = LitePal.findAll&lt;Book&gt;()\n *\n * Note that the associated models won't be loaded by default considering\n * the efficiency, but you can do that by using\n * [LitePal.findAll] with isEager parameter.\n *\n * @param ids\n * Which records to query. Or do not pass it to find all records.\n * @return An object list with found data from database, or an empty list.\n */\ninline fun <reified T> LitePal.findAll(vararg ids: Long) = findAll(T::class.java, *ids)\n\n/**\n * Basically same as [LitePal.findAll] but pending to a new thread for executing.\n *\n * @param ids\n * Which records to query. Or do not pass it to find all records.\n * @return A FindMultiExecutor instance.\n */\ninline fun <reified T> LitePal.findAllAsync(vararg ids: Long) = findAllAsync(T::class.java, *ids)\n\n/**\n * It is mostly same as [LitePal.findAll] but an\n * isEager parameter. If set true the associated models will be loaded as well.\n *\n * Note that isEager will only work for one deep level relation, considering the query efficiency.\n * You have to implement on your own if you need to load multiple deepness of relation at once.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @param ids\n * Which records to query. Or do not pass it to find all records.\n * @return An object list with found data from database, or an empty list.\n */\ninline fun <reified T> LitePal.findAll(isEager: Boolean, vararg ids: Long) = findAll(T::class.java, isEager, *ids)\n\n/**\n * Basically same as [LitePal.findAll] but pending to a new thread for executing.\n *\n * @param isEager\n * True to load the associated models, false not.\n * @param ids\n * Which records to query. Or do not pass it to find all records.\n * @return A FindMultiExecutor instance.\n */\ninline fun <reified T> LitePal.findAllAsync(isEager: Boolean, vararg ids: Long) = findAllAsync(T::class.java, isEager, *ids)\n\n/**\n * Deletes the record in the database by id.\n *\n * The data in other tables which is referenced with the record will be\n * removed too.\n *\n * LitePal.delete&lt;Person&gt;(1)\n *\n * This means that the record 1 in person table will be removed.\n *\n * @param id\n * Which record to delete.\n * @return The number of rows affected. Including cascade delete rows.\n */\ninline fun <reified T> LitePal.delete(id: Long) = delete(T::class.java, id)\n\n/**\n * Basically same as [LitePal.delete] but pending to a new thread for executing.\n *\n * @param id\n * Which record to delete.\n * @return A UpdateOrDeleteExecutor instance.\n */\ninline fun <reified T> LitePal.deleteAsync(id: Long) = deleteAsync(T::class.java, id)\n\n/**\n * Deletes all records with details given if they match a set of conditions\n * supplied. This method constructs a single SQL DELETE statement and sends\n * it to the database.\n *\n * LitePal.deleteAll&lt;Person&gt;(&quot;name = ? and age = ?&quot;, &quot;Tom&quot;, &quot;14&quot;)\n *\n * This means that all the records which name is Tom and age is 14 will be\n * removed.\n *\n * @param conditions\n * A string array representing the WHERE part of an SQL\n * statement. First parameter is the WHERE clause to apply when\n * deleting. The way of specifying place holders is to insert one\n * or more question marks in the SQL. The first question mark is\n * replaced by the second element of the array, the next question\n * mark by the third, and so on. Passing empty string will delete\n * all rows.\n * @return The number of rows affected.\n */\ninline fun <reified T> LitePal.deleteAll(vararg conditions: String?) = deleteAll(T::class.java, *conditions)\n\n/**\n * Basically same as [LitePal.deleteAll] but pending to a new thread for executing.\n *\n * @param conditions\n * A string array representing the WHERE part of an SQL\n * statement. First parameter is the WHERE clause to apply when\n * deleting. The way of specifying place holders is to insert one\n * or more question marks in the SQL. The first question mark is\n * replaced by the second element of the array, the next question\n * mark by the third, and so on. Passing empty string will delete\n * all rows.\n * @return A UpdateOrDeleteExecutor instance.\n */\ninline fun <reified T> LitePal.deleteAllAsync(vararg conditions: String?) = deleteAllAsync(T::class.java, *conditions)\n\n/**\n * Updates the corresponding record by id with ContentValues. Returns the\n * number of affected rows.\n *\n * val cv = ContentValues()\n *\n * cv.put(&quot;name&quot;, &quot;Jim&quot;)\n *\n * LitePal.update&lt;Person&gt;(cv, 1)\n *\n * This means that the name of record 1 will be updated into Jim.\n *\n * @param values\n * A map from column names to new column values. null is a valid\n * value that will be translated to NULL.\n * @param id\n * Which record to update.\n * @return The number of rows affected.\n */\ninline fun <reified T> LitePal.update(values: ContentValues, id: Long) = update(T::class.java, values, id)\n\n/**\n * Basically same as [LitePal.update] but pending to a new thread for executing.\n *\n * @param values\n * A map from column names to new column values. null is a valid\n * value that will be translated to NULL.\n * @param id\n * Which record to update.\n * @return A UpdateOrDeleteExecutor instance.\n */\ninline fun <reified T> LitePal.updateAsync(values: ContentValues, id: Long) = updateAsync(T::class.java, values, id)\n\n/**\n * Updates all records with details given if they match a set of conditions\n * supplied. This method constructs a single SQL UPDATE statement and sends\n * it to the database.\n *\n * val cv = ContentValues()\n *\n * cv.put(&quot;name&quot;, &quot;Jim&quot;)\n *\n * LitePal.update&lt;Person&gt;(cv, &quot;name = ?&quot;, &quot;Tom&quot;)\n *\n * This means that all the records which name is Tom will be updated into\n * Jim.\n *\n * @param values\n * A map from column names to new column values. null is a valid\n * value that will be translated to NULL.\n * @param conditions\n * A string array representing the WHERE part of an SQL\n * statement. First parameter is the WHERE clause to apply when\n * updating. The way of specifying place holders is to insert one\n * or more question marks in the SQL. The first question mark is\n * replaced by the second element of the array, the next question\n * mark by the third, and so on. Passing empty string will update\n * all rows.\n * @return The number of rows affected.\n */\ninline fun <reified T> LitePal.updateAll(values: ContentValues, vararg conditions: String?) = updateAll(T::class.java, values, *conditions)\n\n/**\n * Basically same as [LitePal.updateAll] but pending to a new thread for executing.\n *\n * @param values\n * A map from column names to new column values. null is a valid\n * value that will be translated to NULL.\n * @param conditions\n * A string array representing the WHERE part of an SQL\n * statement. First parameter is the WHERE clause to apply when\n * updating. The way of specifying place holders is to insert one\n * or more question marks in the SQL. The first question mark is\n * replaced by the second element of the array, the next question\n * mark by the third, and so on. Passing empty string will update\n * all rows.\n * @return A UpdateOrDeleteExecutor instance.\n */\ninline fun <reified T> LitePal.updateAllAsync(values: ContentValues, vararg conditions: String?) = updateAllAsync(T::class.java, values, *conditions)\n\n/**\n * Check if the specified conditions data already exists in the table.\n * @param conditions\n * A filter declaring which data to check. Exactly same use as\n * [LitePal.where], except null conditions will result in false.\n * @return Return true if the specified conditions data already exists in the table.\n * False otherwise. Null conditions will result in false.\n */\ninline fun <reified T> LitePal.isExist(vararg conditions: String?) = isExist(T::class.java, *conditions)"
  },
  {
    "path": "kotlin/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">LitePal</string>\n</resources>\n"
  },
  {
    "path": "sample/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "sample/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\n\nandroid {\n    compileSdkVersion 31\n\n    defaultConfig {\n        applicationId \"org.litepal.litepalsample\"\n        minSdkVersion 15\n        targetSdkVersion 31\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation project(':core')\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n    implementation 'androidx.appcompat:appcompat:1.2.0'\n    testImplementation 'junit:junit:4.13.1'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.2'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'\n}\n"
  },
  {
    "path": "sample/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /home/tony/Android/Sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Book.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.crud.LitePalSupport;\n\nimport java.io.Serializable;\n\npublic class Book extends LitePalSupport implements Serializable{\n\n\tprivate static final long serialVersionUID = 9040804172147110007L;\n\n\tprivate long id;\n\n\tprivate String bookName;\n\n\tprivate Integer pages;\n\n\tprivate double price;\n\n\tprivate char level;\n\n\tprivate short isbn;\n\n\tprivate boolean isPublished;\n\n\tprivate float area;\n\t\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getBookName() {\n\t\treturn bookName;\n\t}\n\n\tpublic void setBookName(String bookName) {\n\t\tthis.bookName = bookName;\n\t}\n\n\tpublic Integer getPages() {\n\t\treturn pages;\n\t}\n\n\tpublic void setPages(Integer pages) {\n\t\tthis.pages = pages;\n\t}\n\n\tpublic double getPrice() {\n\t\treturn price;\n\t}\n\n\tpublic void setPrice(double price) {\n\t\tthis.price = price;\n\t}\n\n\tpublic char getLevel() {\n\t\treturn level;\n\t}\n\n\tpublic void setLevel(char level) {\n\t\tthis.level = level;\n\t}\n\n\tpublic short getIsbn() {\n\t\treturn isbn;\n\t}\n\n\tpublic void setIsbn(short isbn) {\n\t\tthis.isbn = isbn;\n\t}\n\n\tpublic boolean isPublished() {\n\t\treturn isPublished;\n\t}\n\n\tpublic void setPublished(boolean isPublished) {\n\t\tthis.isPublished = isPublished;\n\t}\n\n\tpublic float getArea() {\n\t\treturn area;\n\t}\n\n\tpublic void setArea(float area) {\n\t\tthis.area = area;\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Cellphone.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.annotation.Column;\nimport org.litepal.crud.LitePalSupport;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Cellphone extends LitePalSupport {\n\n\tprivate Long id;\n\n\t@Column(index = true)\n\tpublic String brand;\n\n\tprivate Character inStock;\n\n\tprotected Double price;\n\n    @Column(unique = true, nullable = false)\n    String serial;\n\n    @Column(nullable = true, defaultValue = \"0.0.0.0\")\n    private String mac;\n\n    @Column(ignore = true)\n    private String uuid;\n\n    private List<WeiboMessage> messages = new ArrayList<>();\n\n\tpublic Long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(Long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getBrand() {\n\t\treturn brand;\n\t}\n\n\tpublic void setBrand(String brand) {\n\t\tthis.brand = brand;\n\t}\n\n\tpublic Character getInStock() {\n\t\treturn inStock;\n\t}\n\n\tpublic void setInStock(Character inStock) {\n\t\tthis.inStock = inStock;\n\t}\n\n\tpublic Double getPrice() {\n\t\treturn price;\n\t}\n\n\tpublic void setPrice(Double price) {\n\t\tthis.price = price;\n\t}\n\n    public String getSerial() {\n        return serial;\n    }\n\n    public void setSerial(String serial) {\n        this.serial = serial;\n    }\n\n    public String getMac() {\n        return mac;\n    }\n\n    public void setMac(String mac) {\n        this.mac = mac;\n    }\n\n    public String getUuid() {\n        return uuid;\n    }\n\n    public void setUuid(String uuid) {\n        this.uuid = uuid;\n    }\n\n    public List<WeiboMessage> getMessages() {\n        return messages;\n    }\n\n    public void setMessages(List<WeiboMessage> messages) {\n        this.messages = messages;\n    }\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Classroom.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.crud.LitePalSupport;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class Classroom extends LitePalSupport {\n\tprivate int _id;\n\n\tprivate String name;\n\n    private List<String> news = new ArrayList<>();\n\n    private List<Integer> numbers = new ArrayList<>();\n\n\tprivate Set<Student> studentCollection = new HashSet<Student>();\n\n\tprivate List<Teacher> teachers = new ArrayList<Teacher>();\n\n\t/**\n\t * @return the _id\n\t */\n\tpublic int get_id() {\n\t\treturn _id;\n\t}\n\n\t/**\n\t * @param _id\n\t *            the _id to set\n\t */\n\tpublic void set_id(int _id) {\n\t\tthis._id = _id;\n\t}\n\n\t/**\n\t * @return the name\n\t */\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t/**\n\t * @param name\n\t *            the name to set\n\t */\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic Set<Student> getStudentCollection() {\n\t\treturn studentCollection;\n\t}\n\n\tpublic void setStudentCollection(Set<Student> studentCollection) {\n\t\tthis.studentCollection = studentCollection;\n\t}\n\n\tpublic List<Teacher> getTeachers() {\n\t\treturn teachers;\n\t}\n\n\tpublic void setTeachers(List<Teacher> teachers) {\n\t\tthis.teachers = teachers;\n\t}\n\n    public List<String> getNews() {\n        return news;\n    }\n\n    public void setNews(List<String> news) {\n        this.news = news;\n    }\n\n    public List<Integer> getNumbers() {\n        return numbers;\n    }\n\n    public void setNumbers(List<Integer> numbers) {\n        this.numbers = numbers;\n    }\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Computer.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.crud.LitePalSupport;\n\npublic class Computer extends LitePalSupport {\n\t\n\tprivate long id;\n\n\tprivate String brand;\n\n\tprivate double price;\n\n\tpublic Computer(String brand, double price) {\n\t\tthis.brand = brand;\n\t\tthis.price = price;\n\t}\n\t\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\tpublic String getBrand() {\n\t\treturn brand;\n\t}\n\n\tpublic void setBrand(String brand) {\n\t\tthis.brand = brand;\n\t}\n\n\tpublic double getPrice() {\n\t\treturn price;\n\t}\n\n\tpublic void setPrice(double price) {\n\t\tthis.price = price;\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Headset.java",
    "content": "package com.litepaltest.model;\n\npublic class Headset {\n\n    private int id;\n\n    private String brand;\n\n    private String color;\n\n    private int type;\n\n    private double price;\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getBrand() {\n        return brand;\n    }\n\n    public void setBrand(String brand) {\n        this.brand = brand;\n    }\n\n    public String getColor() {\n        return color;\n    }\n\n    public void setColor(String color) {\n        this.color = color;\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public void setType(int type) {\n        this.type = type;\n    }\n\n    public double getPrice() {\n        return price;\n    }\n\n    public void setPrice(double price) {\n        this.price = price;\n    }\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/IdCard.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.crud.LitePalSupport;\n\npublic class IdCard extends LitePalSupport {\n\tprivate int id;\n\tprivate String number;\n\tprivate String address;\n\tprivate Student student;\n\tprivate long serial;\n//\tprivate Teacher teacher;\n\n\t/**\n\t * @return the id\n\t */\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\t/**\n\t * @param id\n\t *            the id to set\n\t */\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\n\t/**\n\t * @return the number\n\t */\n\tpublic String getNumber() {\n\t\treturn number;\n\t}\n\n\t/**\n\t * @param number\n\t *            the number to set\n\t */\n\tpublic void setNumber(String number) {\n\t\tthis.number = number;\n\t}\n\n\t/**\n\t * @return the address\n\t */\n\tpublic String getAddress() {\n\t\treturn address;\n\t}\n\n\t/**\n\t * @param address\n\t *            the address to set\n\t */\n\tpublic void setAddress(String address) {\n\t\tthis.address = address;\n\t}\n\n\t/**\n\t * @return the student\n\t */\n\tpublic Student getStudent() {\n\t\treturn student;\n\t}\n\n\t/**\n\t * @param student the student to set\n\t */\n\tpublic void setStudent(Student student) {\n\t\tthis.student = student;\n\t}\n\n\tpublic long getSerial() {\n\t\treturn serial;\n\t}\n\n\tpublic void setSerial(long serial) {\n\t\tthis.serial = serial;\n\t}\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Message.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.annotation.Column;\nimport org.litepal.crud.LitePalSupport;\n\npublic class Message extends LitePalSupport {\n\n    private int id;\n\n    private String content;\n\n    public int type;\n\n    @Column(ignore = true)\n    String title;\n\n    public String getContent() {\n        return content;\n    }\n\n    public void setContent(String content) {\n        this.content = content;\n    }\n\n    public int getType() {\n        return type;\n    }\n\n    public void setType(int type) {\n        this.type = type;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Product.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.crud.LitePalSupport;\n\npublic class Product extends LitePalSupport{\n\t\n\tprivate int id;\n\t\n\tprivate String brand;\n\n\tprivate double price;\n\n    private byte[] pic;\n\t\n\tpublic Product() {\n\t}\n\t\n\tpublic Product(Product p) {\n\t}\n\t\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\t\n\tpublic String getBrand() {\n\t\treturn brand;\n\t}\n\n\tpublic void setBrand(String brand) {\n\t\tthis.brand = brand;\n\t}\n\n\tpublic double getPrice() {\n\t\treturn price;\n\t}\n\n\tpublic void setPrice(double price) {\n\t\tthis.price = price;\n\t}\n\n    public byte[] getPic() {\n        return pic;\n    }\n\n    public void setPic(byte[] pic) {\n        this.pic = pic;\n    }\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Student.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.annotation.Column;\nimport org.litepal.crud.LitePalSupport;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\npublic class Student extends LitePalSupport {\n\tprivate int id;\n\tprivate String name;\n\tprivate int age;\n\tprivate Date birthday;\n\n\t@Column(defaultValue = \"1589203961859\")\n\tprivate Date schoolDate;\n\tprivate Classroom classroom;\n\tprivate IdCard idcard;\n\n\t// private Teacher teacher;\n\tprivate List<Teacher> teachers = new ArrayList<Teacher>();\n\n\t// private IdCard idCard;\n\n\t// private double salary;\n\t// private String address;\n\t// private Teacher teacher;\n\t// private int classroom_id;\n\t// private List lists;\n\t// private Context context;\n\t// private IdCard idCard;\n\t// private List<Classroom> classrooms;\n\n\t/**\n\t * @return the name\n\t */\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t/**\n\t * @return the classroom\n\t */\n\tpublic Classroom getClassroom() {\n\t\treturn classroom;\n\t}\n\n\t/**\n\t * @param classroom\n\t *            the classroom to set\n\t */\n\tpublic void setClassroom(Classroom classroom) {\n\t\tthis.classroom = classroom;\n\t}\n\n\t/**\n\t * @return the id\n\t */\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\t/**\n\t * @param id\n\t *            the id to set\n\t */\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\n\t/**\n\t * @param name\n\t *            the name to set\n\t */\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t/**\n\t * @return the age\n\t */\n\tpublic int getAge() {\n\t\treturn age;\n\t}\n\n\t/**\n\t * @param age\n\t *            the age to set\n\t */\n\tpublic void setAge(int age) {\n\t\tthis.age = age;\n\t}\n\n\t/**\n\t * @return the birthday\n\t */\n\tpublic Date getBirthday() {\n\t\treturn birthday;\n\t}\n\n\t/**\n\t * @param birthday\n\t *            the birthday to set\n\t */\n\tpublic void setBirthday(Date birthday) {\n\t\tthis.birthday = birthday;\n\t}\n\n\t/**\n\t * @return the idcard\n\t */\n\tpublic IdCard getIdcard() {\n\t\treturn idcard;\n\t}\n\n\t/**\n\t * @param idcard\n\t *            the idcard to set\n\t */\n\tpublic void setIdcard(IdCard idcard) {\n\t\tthis.idcard = idcard;\n\t}\n\n\tpublic List<Teacher> getTeachers() {\n\t\treturn teachers;\n\t}\n\n\tpublic void setTeachers(List<Teacher> teachers) {\n\t\tthis.teachers = teachers;\n\t}\n\n\t// /**\n\t// * @return the idCard\n\t// */\n\t// public IdCard getIdCard() {\n\t// return idCard;\n\t// }\n\t//\n\t// /**\n\t// * @param idCard the idCard to set\n\t// */\n\t// public void setIdCard(IdCard idCard) {\n\t// this.idCard = idCard;\n\t// }\n\n\n\tpublic Date getSchoolDate() {\n\t\treturn schoolDate;\n\t}\n\n\tpublic void setSchoolDate(Date schoolDate) {\n\t\tthis.schoolDate = schoolDate;\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/Teacher.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.crud.LitePalSupport;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Teacher extends LitePalSupport {\n\n\tprivate int id;\n\n\tprivate String teacherName = \"\";\n\n\tprivate boolean sex = true;\n\n\tprivate int age = 22;\n\n\tprivate int teachYears;\n\n\tprivate IdCard idCard;\n\n\t// private Student student;\n\n\tprivate List<Student> students = new ArrayList<Student>();\n\n\t// /**\n\t// * @return the students\n\t// */\n\t// public List<Student> getStudents() {\n\t// return students;\n\t// }\n\t//\n\t// /**\n\t// * @param students the students to set\n\t// */\n\t// public void setStudents(List<Student> students) {\n\t// this.students = students;\n\t// }\n\n\t/**\n\t * @return the teacherName\n\t */\n\tpublic String getTeacherName() {\n\t\treturn teacherName;\n\t}\n\n\t/**\n\t * @return the idCard\n\t */\n\tpublic IdCard getIdCard() {\n\t\treturn idCard;\n\t}\n\n\t/**\n\t * @param idCard\n\t *            the idCard to set\n\t */\n\tpublic void setIdCard(IdCard idCard) {\n\t\tthis.idCard = idCard;\n\t}\n\n\t/**\n\t * @param teacherName\n\t *            the teacherName to set\n\t */\n\tpublic void setTeacherName(String teacherName) {\n\t\tthis.teacherName = teacherName;\n\t}\n\n\t/**\n\t * @return the age\n\t */\n\tpublic int getAge() {\n\t\treturn age;\n\t}\n\n\t/**\n\t * @param age\n\t *            the age to set\n\t */\n\tpublic void setAge(int age) {\n\t\tthis.age = age;\n\t}\n\n\t/**\n\t * @return the teachYears\n\t */\n\tpublic int getTeachYears() {\n\t\treturn teachYears;\n\t}\n\n\t/**\n\t * @param teachYears\n\t *            the teachYears to set\n\t */\n\tpublic void setTeachYears(int teachYears) {\n\t\tthis.teachYears = teachYears;\n\t}\n\n\t/**\n\t * @return the id\n\t */\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\t/**\n\t * @param id\n\t *            the id to set\n\t */\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\n\t/**\n\t * @return the sex\n\t */\n\tpublic boolean isSex() {\n\t\treturn sex;\n\t}\n\n\t/**\n\t * @param sex\n\t *            the sex to set\n\t */\n\tpublic void setSex(boolean sex) {\n\t\tthis.sex = sex;\n\t}\n\n\tpublic List<Student> getStudents() {\n\t\treturn students;\n\t}\n\n\tpublic void setStudents(List<Student> students) {\n\t\tthis.students = students;\n\t}\n\n\t// private Student student;\n\n\t// private List<Student> students = new ArrayList<Student>();\n\t//\n\t// private IdCard idCard;\n\n\t// public String getName() {\n\t// return name;\n\t// }\n\t//\n\t// public void setName(String name) {\n\t// this.name = name;\n\t// }\n\t//\n\t// public boolean isSex() {\n\t// return sex;\n\t// }\n\t//\n\t// public void setSex(boolean sex) {\n\t// this.sex = sex;\n\t// }\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/WeChatMessage.java",
    "content": "package com.litepaltest.model;\n\npublic class WeChatMessage extends Message {\n\n    private String friend;\n\n    public String getFriend() {\n        return friend;\n    }\n\n    public void setFriend(String friend) {\n        this.friend = friend;\n    }\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/model/WeiboMessage.java",
    "content": "package com.litepaltest.model;\n\nimport org.litepal.annotation.Column;\n\npublic class WeiboMessage extends Message {\n\n    private String follower;\n\n    @Column(ignore = true)\n    private int number;\n\n    private Cellphone cellphone;\n\n    public String getFollower() {\n        return follower;\n    }\n\n    public void setFollower(String follower) {\n        this.follower = follower;\n    }\n\n    public int getNumber() {\n        return number;\n    }\n\n    public void setNumber(int number) {\n        this.number = number;\n    }\n\n    public Cellphone getCellphone() {\n        return cellphone;\n    }\n\n    public void setCellphone(Cellphone cellphone) {\n        this.cellphone = cellphone;\n    }\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/LitePalTestCase.java",
    "content": "package com.litepaltest.test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.util.BaseUtility;\nimport org.litepal.util.DBUtility;\n\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Book;\nimport com.litepaltest.model.Cellphone;\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.Computer;\nimport com.litepaltest.model.IdCard;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\n\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class LitePalTestCase {\n\n\tprotected void assertM2M(String table1, String table2, long id1, long id2) {\n\t\tassertTrue(isIntermediateDataCorrect(table1, table2, id1, id2));\n\t}\n\n\tprotected void assertM2MFalse(String table1, String table2, long id1, long id2) {\n\t\tassertFalse(isIntermediateDataCorrect(table1, table2, id1, id2));\n\t}\n\n\t/**\n\t * \n\t * @param table1\n\t *            Table without foreign key.\n\t * @param table2\n\t *            Table with foreign key.\n\t * @param table1Id\n\t *            id of table1.\n\t * @param table2Id\n\t *            id of table2.\n\t * @return success or failed.\n\t */\n\tprotected boolean isFKInsertCorrect(String table1, String table2, long table1Id, long table2Id) {\n\t\tSQLiteDatabase db = Connector.getDatabase();\n\t\ttry (Cursor cursor = db.query(table2, null, \"id = ?\", new String[]{String.valueOf(table2Id)},\n\t\t\t\tnull, null, null)) {\n\t\t\tcursor.moveToFirst();\n\t\t\tlong fkId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseUtility.changeCase(table1\n\t\t\t\t\t+ \"_id\")));\n\t\t\treturn fkId == table1Id;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected boolean isIntermediateDataCorrect(String table1, String table2, long table1Id,\n\t\t\tlong table2Id) {\n\t\tSQLiteDatabase db = Connector.getDatabase();\n\t\tCursor cursor = null;\n\t\ttry {\n\t\t\tString where = table1 + \"_id = ? and \" + table2 + \"_id = ?\";\n\t\t\tcursor = db.query(DBUtility.getIntermediateTableName(table1, table2), null, where,\n\t\t\t\t\tnew String[] { String.valueOf(table1Id), String.valueOf(table2Id) }, null,\n\t\t\t\t\tnull, null);\n\t\t\treturn cursor.getCount() == 1;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t} finally {\n\t\t\tif (cursor != null) {\n\t\t\t\tcursor.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected long getForeignKeyValue(String tableWithFK, String tableWithoutFK, long id) {\n\t\tCursor cursor = Connector.getDatabase().query(tableWithFK, null, \"id = ?\",\n                new String[]{String.valueOf(id)}, null, null, null);\n\t\tlong foreignKeyId = 0;\n\t\tif (cursor.moveToFirst()) {\n\t\t\tforeignKeyId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseUtility\n\t\t\t\t\t.changeCase(tableWithoutFK + \"_id\")));\n\t\t}\n\t\tcursor.close();\n\t\treturn foreignKeyId;\n\t}\n\n\tprotected boolean isDataExists(String table, long id) {\n\t\tSQLiteDatabase db = Connector.getDatabase();\n\t\ttry (Cursor cursor = db.query(table, null, \"id = ?\", new String[]{String.valueOf(id)}, null,\n\t\t\t\tnull, null)) {\n\t\t\treturn cursor.getCount() == 1;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn false;\n\t}\n\n\tprotected String getTableName(Object object) {\n\t\treturn DBUtility.getTableNameByClassName(object.getClass().getName());\n\t}\n\n    protected String getTableName(Class<?> c) {\n        return DBUtility.getTableNameByClassName(c.getName());\n    }\n\n\tprotected int getRowsCount(String tableName) {\n\t\tint count = 0;\n\t\tCursor c = Connector.getDatabase().query(tableName, null, null, null, null, null, null);\n\t\tcount = c.getCount();\n\t\tc.close();\n\t\treturn count;\n\t}\n\n\tprotected List<Book> getBooks(String[] columns, String selection, String[] selectionArgs,\n\t\t\tString groupBy, String having, String orderBy, String limit) {\n\t\tList<Book> books = new ArrayList<Book>();\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(Book.class), columns, selection, selectionArgs,\n\t\t\t\tgroupBy, having, orderBy, limit);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tdo {\n\t\t\t\tlong id = cursor.getLong(cursor.getColumnIndexOrThrow(\"id\"));\n\t\t\t\tString bookName = cursor.getString(cursor.getColumnIndexOrThrow(\"bookname\"));\n\t\t\t\tInteger pages = null;\n\t\t\t\tif (!cursor.isNull(cursor.getColumnIndexOrThrow(\"pages\"))) {\n\t\t\t\t\tpages = cursor.getInt(cursor.getColumnIndexOrThrow(\"pages\"));\n\t\t\t\t}\n\t\t\t\tdouble price = cursor.getDouble(cursor.getColumnIndexOrThrow(\"price\"));\n\t\t\t\tchar level = cursor.getString(cursor.getColumnIndexOrThrow(\"level\")).charAt(0);\n\t\t\t\tshort isbn = cursor.getShort(cursor.getColumnIndexOrThrow(\"isbn\"));\n\t\t\t\tfloat area = cursor.getFloat(cursor.getColumnIndexOrThrow(\"area\"));\n\t\t\t\tboolean isPublished = cursor.getInt(cursor.getColumnIndexOrThrow(\"ispublished\")) == 1;\n\t\t\t\tBook book = new Book();\n\t\t\t\tbook.setId(id);\n\t\t\t\tbook.setBookName(bookName);\n\t\t\t\tbook.setPages(pages);\n\t\t\t\tbook.setPrice(price);\n\t\t\t\tbook.setLevel(level);\n\t\t\t\tbook.setIsbn(isbn);\n\t\t\t\tbook.setArea(area);\n\t\t\t\tbook.setPublished(isPublished);\n\t\t\t\tbooks.add(book);\n\t\t\t} while (cursor.moveToNext());\n\t\t}\n\t\tcursor.close();\n\t\treturn books;\n\t}\n\n\tprotected Classroom getClassroom(long id) {\n\t\tClassroom c = null;\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(Classroom.class), null, \"id = ?\",\n\t\t\t\tnew String[] { String.valueOf(id) }, null, null, null);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tc = new Classroom();\n\t\t\tString name = cursor.getString(cursor.getColumnIndexOrThrow(\"name\"));\n\t\t\tc.setName(name);\n\t\t}\n\t\tcursor.close();\n\t\treturn c;\n\t}\n\n\tprotected IdCard getIdCard(long id) {\n\t\tIdCard card = null;\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(IdCard.class), null, \"id = ?\",\n\t\t\t\tnew String[] { String.valueOf(id) }, null, null, null);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tcard = new IdCard();\n\t\t\tString address = cursor.getString(cursor.getColumnIndexOrThrow(\"address\"));\n\t\t\tString number = cursor.getString(cursor.getColumnIndexOrThrow(\"number\"));\n\t\t\tcard.setAddress(address);\n\t\t\tcard.setNumber(number);\n\t\t}\n\t\tcursor.close();\n\t\treturn card;\n\t}\n\n\tprotected Computer getComputer(long id) {\n\t\tComputer computer = null;\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(Computer.class), null, \"id = ?\",\n\t\t\t\tnew String[] { String.valueOf(id) }, null, null, null);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tcomputer = new Computer(\"\", 0);\n\t\t\tdouble newPrice = cursor.getDouble(cursor.getColumnIndexOrThrow(\"price\"));\n\t\t\tString brand = cursor.getString(cursor.getColumnIndexOrThrow(\"brand\"));\n\t\t\tcomputer.setBrand(brand);\n\t\t\tcomputer.setPrice(newPrice);\n\t\t}\n\t\tcursor.close();\n\t\treturn computer;\n\t}\n\n\tprotected Cellphone getCellPhone(long id) {\n\t\tCellphone cellPhone = null;\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(Cellphone.class), null, \"id = ?\",\n\t\t\t\tnew String[] { String.valueOf(id) }, null, null, null);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tcellPhone = new Cellphone();\n\t\t\tdouble newPrice = cursor.getDouble(cursor.getColumnIndexOrThrow(\"price\"));\n\t\t\tchar inStock = cursor.getString(cursor.getColumnIndexOrThrow(\"instock\")).charAt(0);\n\t\t\tString brand = cursor.getString(cursor.getColumnIndexOrThrow(\"brand\"));\n\t\t\tcellPhone.setBrand(brand);\n\t\t\tcellPhone.setInStock(inStock);\n\t\t\tcellPhone.setPrice(newPrice);\n\t\t}\n\t\tcursor.close();\n\t\treturn cellPhone;\n\t}\n\n\tprotected Teacher getTeacher(long id) {\n\t\tTeacher teacher = null;\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(Teacher.class), null, \"id = ?\",\n\t\t\t\tnew String[] { String.valueOf(id) }, null, null, null);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tteacher = new Teacher();\n\t\t\tString teacherName = cursor.getString(cursor.getColumnIndexOrThrow(\"teachername\"));\n\t\t\tint teachYears = cursor.getInt(cursor.getColumnIndexOrThrow(\"teachyears\"));\n\t\t\tint age = cursor.getInt(cursor.getColumnIndexOrThrow(\"age\"));\n\t\t\tint sex = cursor.getInt(cursor.getColumnIndexOrThrow(\"sex\"));\n\t\t\tteacher.setTeacherName(teacherName);\n\t\t\tteacher.setTeachYears(teachYears);\n\t\t\tteacher.setAge(age);\n\t\t\tif (sex == 0) {\n\t\t\t\tteacher.setSex(false);\n\t\t\t} else if (sex == 1) {\n\t\t\t\tteacher.setSex(true);\n\t\t\t}\n\t\t}\n\t\tcursor.close();\n\t\treturn teacher;\n\t}\n\n\tprotected Student getStudent(long id) {\n\t\tStudent student = null;\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(Student.class), null, \"id = ?\",\n\t\t\t\tnew String[] { String.valueOf(id) }, null, null, null);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tstudent = new Student();\n\t\t\tString name = cursor.getString(cursor.getColumnIndexOrThrow(\"name\"));\n\t\t\tint age = cursor.getInt(cursor.getColumnIndexOrThrow(\"age\"));\n\t\t\tstudent.setName(name);\n\t\t\tstudent.setAge(age);\n\t\t}\n\t\tcursor.close();\n\t\treturn student;\n\t}\n\n\tprotected List<Teacher> getTeachers(int[] ids) {\n\t\tList<Teacher> teachers = new ArrayList<>();\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(Teacher.class), null, getWhere(ids), null, null,\n\t\t\t\tnull, null);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tTeacher t = new Teacher();\n\t\t\tString teacherName = cursor.getString(cursor.getColumnIndexOrThrow(\"teachername\"));\n\t\t\tint teachYears = cursor.getInt(cursor.getColumnIndexOrThrow(\"teachyears\"));\n\t\t\tint age = cursor.getInt(cursor.getColumnIndexOrThrow(\"age\"));\n\t\t\tint sex = cursor.getInt(cursor.getColumnIndexOrThrow(\"sex\"));\n\t\t\tt.setTeacherName(teacherName);\n\t\t\tt.setTeachYears(teachYears);\n\t\t\tt.setAge(age);\n\t\t\tif (sex == 0) {\n\t\t\t\tt.setSex(false);\n\t\t\t} else if (sex == 1) {\n\t\t\t\tt.setSex(true);\n\t\t\t}\n\t\t\tteachers.add(t);\n\t\t}\n\t\tcursor.close();\n\t\treturn teachers;\n\t}\n\n\tprotected List<Student> getStudents(int[] ids) {\n\t\tList<Student> students = new ArrayList<>();\n\t\tCursor cursor = Connector.getDatabase().query(getTableName(Student.class), null, getWhere(ids), null, null,\n\t\t\t\tnull, null);\n\t\tif (cursor.moveToFirst()) {\n\t\t\tStudent s = new Student();\n\t\t\tString name = cursor.getString(cursor.getColumnIndexOrThrow(\"name\"));\n\t\t\tint age = cursor.getInt(cursor.getColumnIndexOrThrow(\"age\"));\n\t\t\ts.setName(name);\n\t\t\ts.setAge(age);\n\t\t\tstudents.add(s);\n\t\t}\n\t\tcursor.close();\n\t\treturn students;\n\t}\n\n\tprivate String getWhere(int[] ids) {\n\t\tStringBuilder where = new StringBuilder();\n\t\tboolean needOr = false;\n\t\tfor (int id : ids) {\n\t\t\tif (needOr) {\n\t\t\t\twhere.append(\" or \");\n\t\t\t}\n\t\t\twhere.append(\"id = \").append(id);\n\t\t\tneedOr = true;\n\t\t}\n\t\treturn where.toString();\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/MultiDatabaseTest.java",
    "content": "package com.litepaltest.test;\n\nimport android.database.sqlite.SQLiteDatabase;\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.Computer;\nimport com.litepaltest.model.Headset;\nimport com.litepaltest.model.Product;\n\nimport org.junit.Test;\nimport org.litepal.LitePal;\nimport org.litepal.LitePalDB;\nimport org.litepal.util.DBUtility;\n\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertTrue;\n\n/**\n * @author guolin\n * @since 2016/11/10\n */\n@SmallTest\npublic class MultiDatabaseTest extends LitePalTestCase {\n\n    @Test\n    public void testMultiDatabase() {\n        LitePal.deleteDatabase(\"db2\");\n        SQLiteDatabase db = LitePal.getDatabase();\n        assertTrue(DBUtility.isTableExists(\"Album\", db));\n        assertTrue(DBUtility.isTableExists(\"Song\", db));\n        assertTrue(DBUtility.isTableExists(\"Singer\", db));\n        assertTrue(DBUtility.isTableExists(\"Classroom\", db));\n        assertTrue(DBUtility.isTableExists(\"Teacher\", db));\n        assertTrue(DBUtility.isTableExists(\"IdCard\", db));\n        assertTrue(DBUtility.isTableExists(\"Student\", db));\n        assertTrue(DBUtility.isTableExists(\"Cellphone\", db));\n        assertTrue(DBUtility.isTableExists(\"Computer\", db));\n        assertTrue(DBUtility.isTableExists(\"Book\", db));\n        assertTrue(DBUtility.isTableExists(\"Product\", db));\n        assertTrue(DBUtility.isTableExists(\"Headset\", db));\n        assertTrue(DBUtility.isTableExists(\"WeChatMessage\", db));\n        assertTrue(DBUtility.isTableExists(\"WeiboMessage\", db));\n\n        LitePalDB litePalDB = new LitePalDB(\"db2\", 1);\n        litePalDB.addClassName(Classroom.class.getName());\n        litePalDB.addClassName(Product.class.getName());\n        litePalDB.setExternalStorage(true);\n        LitePal.use(litePalDB);\n        db = LitePal.getDatabase();\n        assertFalse(DBUtility.isTableExists(\"Album\", db));\n        assertFalse(DBUtility.isTableExists(\"Song\", db));\n        assertFalse(DBUtility.isTableExists(\"Singer\", db));\n        assertTrue(DBUtility.isTableExists(\"Classroom\", db));\n        assertFalse(DBUtility.isTableExists(\"Teacher\", db));\n        assertFalse(DBUtility.isTableExists(\"IdCard\", db));\n        assertFalse(DBUtility.isTableExists(\"Student\", db));\n        assertFalse(DBUtility.isTableExists(\"Cellphone\", db));\n        assertFalse(DBUtility.isTableExists(\"Computer\", db));\n        assertFalse(DBUtility.isTableExists(\"Book\", db));\n        assertTrue(DBUtility.isTableExists(\"Product\", db));\n        assertFalse(DBUtility.isTableExists(\"Headset\", db));\n        assertFalse(DBUtility.isTableExists(\"WeChatMessage\", db));\n        assertFalse(DBUtility.isTableExists(\"WeiboMessage\", db));\n\n        litePalDB = new LitePalDB(\"db2\", 2);\n        litePalDB.addClassName(Computer.class.getName());\n        litePalDB.addClassName(Product.class.getName());\n        litePalDB.addClassName(Headset.class.getName());\n        litePalDB.setExternalStorage(true);\n        LitePal.use(litePalDB);\n        db = LitePal.getDatabase();\n        assertFalse(DBUtility.isTableExists(\"Album\", db));\n        assertFalse(DBUtility.isTableExists(\"Song\", db));\n        assertFalse(DBUtility.isTableExists(\"Singer\", db));\n        assertFalse(DBUtility.isTableExists(\"Classroom\", db));\n        assertFalse(DBUtility.isTableExists(\"Teacher\", db));\n        assertFalse(DBUtility.isTableExists(\"IdCard\", db));\n        assertFalse(DBUtility.isTableExists(\"Student\", db));\n        assertFalse(DBUtility.isTableExists(\"Cellphone\", db));\n        assertTrue(DBUtility.isTableExists(\"Computer\", db));\n        assertFalse(DBUtility.isTableExists(\"Book\", db));\n        assertTrue(DBUtility.isTableExists(\"Product\", db));\n        assertTrue(DBUtility.isTableExists(\"Headset\", db));\n        assertFalse(DBUtility.isTableExists(\"WeChatMessage\", db));\n        assertFalse(DBUtility.isTableExists(\"WeiboMessage\", db));\n\n        LitePal.useDefault();\n        db = LitePal.getDatabase();\n        assertTrue(DBUtility.isTableExists(\"Album\", db));\n        assertTrue(DBUtility.isTableExists(\"Song\", db));\n        assertTrue(DBUtility.isTableExists(\"Singer\", db));\n        assertTrue(DBUtility.isTableExists(\"Classroom\", db));\n        assertTrue(DBUtility.isTableExists(\"Teacher\", db));\n        assertTrue(DBUtility.isTableExists(\"IdCard\", db));\n        assertTrue(DBUtility.isTableExists(\"Student\", db));\n        assertTrue(DBUtility.isTableExists(\"Cellphone\", db));\n        assertTrue(DBUtility.isTableExists(\"Computer\", db));\n        assertTrue(DBUtility.isTableExists(\"Book\", db));\n        assertTrue(DBUtility.isTableExists(\"Product\", db));\n        assertTrue(DBUtility.isTableExists(\"Headset\", db));\n        assertTrue(DBUtility.isTableExists(\"WeChatMessage\", db));\n        assertTrue(DBUtility.isTableExists(\"WeiboMessage\", db));\n    }\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/annotation/ColumnTest.java",
    "content": "package com.litepaltest.test.annotation;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Cellphone;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.LitePal;\n\nimport java.util.UUID;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertTrue;\n\n/**\n * Created by tony on 15-8-24.\n */\n@SmallTest\npublic class ColumnTest extends LitePalTestCase {\n\n    @Before\n    public void setUp() {\n        LitePal.getDatabase();\n    }\n\n    @Test\n    public void testUnique() {\n        String serial = UUID.randomUUID().toString();\n        for (int i = 0; i < 2; i++) {\n            Cellphone cellphone = new Cellphone();\n            cellphone.setBrand(\"三星\");\n            cellphone.setInStock('Y');\n            cellphone.setPrice(1949.99);\n            cellphone.setSerial(serial);\n            if (i == 0) {\n                assertTrue(cellphone.save());\n            } else if (i == 1) {\n                assertFalse(cellphone.save());\n            }\n        }\n    }\n\n    @Test\n    public void testNotNull() {\n        Cellphone cellphone = new Cellphone();\n        cellphone.setBrand(\"三星\");\n        cellphone.setInStock('Y');\n        cellphone.setPrice(1949.99);\n        assertFalse(cellphone.save());\n        cellphone.setSerial(UUID.randomUUID().toString());\n        assertTrue(cellphone.save());\n    }\n\n    @Test\n    public void testDefaultValue() {\n        Cellphone cellphone = new Cellphone();\n        cellphone.setBrand(\"三星\");\n        cellphone.setInStock('Y');\n        cellphone.setPrice(1949.99);\n        cellphone.setSerial(UUID.randomUUID().toString());\n        assertTrue(cellphone.save());\n        assertEquals(\"0.0.0.0\", LitePal.find(Cellphone.class, cellphone.getId()).getMac());\n        cellphone.setMac(\"192.168.0.1\");\n        assertTrue(cellphone.save());\n        assertEquals(\"192.168.0.1\", LitePal.find(Cellphone.class, cellphone.getId()).getMac());\n    }\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/delete/DeleteKotlinTest.kt",
    "content": "package com.litepaltest.test.crud.delete\n\nimport android.database.sqlite.SQLiteException\nimport androidx.test.filters.SmallTest\nimport com.litepaltest.model.Classroom\nimport com.litepaltest.model.IdCard\nimport com.litepaltest.model.Student\nimport com.litepaltest.model.Teacher\nimport com.litepaltest.test.LitePalTestCase\nimport junit.framework.TestCase.*\nimport org.junit.Before\nimport org.junit.Test\nimport org.litepal.LitePal\nimport org.litepal.exceptions.DataSupportException\nimport org.litepal.extension.delete\nimport org.litepal.extension.deleteAll\nimport org.litepal.extension.find\nimport org.litepal.util.DBUtility\nimport java.util.*\n\n@SmallTest\nclass DeleteKotlinTest : LitePalTestCase() {\n\n    private var gameRoom: Classroom? = null\n\n    private var jude: Student? = null\n\n    private var rose: Student? = null\n\n    private var john: Teacher? = null\n\n    private var mike: Teacher? = null\n\n    private var judeCard: IdCard? = null\n\n    private var roseCard: IdCard? = null\n\n    private var johnCard: IdCard? = null\n\n    private var mikeCard: IdCard? = null\n\n    private var studentTable: String? = null\n\n    private var teacherTable: String? = null\n\n    @Before\n    fun setUp() {\n        studentTable = DBUtility.getTableNameByClassName(Student::class.java.name)\n        teacherTable = DBUtility.getTableNameByClassName(Teacher::class.java.name)\n    }\n\n    @Test\n    fun createClassroomStudentsTeachers() {\n        initGameRoom()\n        initRose()\n        initJude()\n        initMike()\n        initJohn()\n        val students = HashSet<Student>()\n        students.add(rose!!)\n        students.add(jude!!)\n        gameRoom!!.studentCollection = students\n        gameRoom!!.teachers.add(john)\n        gameRoom!!.teachers.add(mike)\n        gameRoom!!.save()\n        rose!!.save()\n        jude!!.save()\n        john!!.save()\n        mike!!.save()\n    }\n\n    @Test\n    fun createStudentsTeachersWithIdCard() {\n        initRose()\n        initJude()\n        initMike()\n        initJohn()\n        rose!!.save()\n        jude!!.save()\n        mike!!.save()\n        john!!.save()\n        roseCard!!.save()\n        judeCard!!.save()\n        mikeCard!!.save()\n        johnCard!!.save()\n    }\n\n    @Test\n    fun createStudentsTeachersWithAssociations() {\n        initRose()\n        initJude()\n        initMike()\n        initJohn()\n        rose!!.teachers.add(john)\n        rose!!.teachers.add(mike)\n        jude!!.teachers.add(mike)\n        rose!!.save()\n        jude!!.save()\n        john!!.save()\n        mike!!.save()\n    }\n\n    @Test\n    fun testDeleteWithNoParameter() {\n        initJude()\n        jude!!.save()\n        val rowsAffected = jude!!.delete()\n        assertEquals(1, rowsAffected)\n        val s = getStudent(jude!!.id.toLong())\n        assertNull(s)\n    }\n\n    @Test\n    fun testDeleteById() {\n        initJude()\n        jude!!.save()\n        val rowsAffected = LitePal.delete<Student>(jude!!.id.toLong())\n        assertEquals(1, rowsAffected)\n        val s = getStudent(jude!!.id.toLong())\n        assertNull(s)\n    }\n\n    @Test\n    fun testDeleteNoSavedModelWithNoParameter() {\n        val tony = Student()\n        tony.name = \"Tony\"\n        tony.age = 23\n        val rowsAffected = tony.delete()\n        assertEquals(0, rowsAffected)\n    }\n\n    @Test\n    fun testDeleteWithNotExistsRecordById() {\n        val rowsAffected = LitePal.delete<Student>(998909)\n        assertEquals(0, rowsAffected)\n    }\n\n    @Test\n    fun testDeleteCascadeM2OAssociationsOnMSideWithNoParameter() {\n        createClassroomStudentsTeachers()\n        val rowsAffected = gameRoom!!.delete()\n        assertEquals(5, rowsAffected)\n        assertNull(getClassroom(gameRoom!!._id.toLong()))\n        assertNull(getStudent(jude!!.id.toLong()))\n        assertNull(getStudent(rose!!.id.toLong()))\n        assertNull(getTeacher(john!!.id.toLong()))\n        assertNull(getTeacher(mike!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteCascadeM2OAssociationsOnMSideById() {\n        createClassroomStudentsTeachers()\n        val rowsAffected = LitePal.delete<Classroom>(gameRoom!!._id.toLong())\n        assertEquals(5, rowsAffected)\n        assertNull(getClassroom(gameRoom!!._id.toLong()))\n        assertNull(getStudent(jude!!.id.toLong()))\n        assertNull(getStudent(rose!!.id.toLong()))\n        assertNull(getTeacher(john!!.id.toLong()))\n        assertNull(getTeacher(mike!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteAllCascadeM2OAssociationsOnMSide() {\n        createClassroomStudentsTeachers()\n        val rowsAffected = LitePal.deleteAll<Classroom>(\"id = ?\", gameRoom!!._id.toString() + \"\")\n        assertEquals(5, rowsAffected)\n        assertNull(getClassroom(gameRoom!!._id.toLong()))\n        assertNull(getStudent(jude!!.id.toLong()))\n        assertNull(getStudent(rose!!.id.toLong()))\n        assertNull(getTeacher(john!!.id.toLong()))\n        assertNull(getTeacher(mike!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteCascadeM2OAssociationsOnOSideWithNoParameter() {\n        createClassroomStudentsTeachers()\n        var rowsAffected = jude!!.delete()\n        assertEquals(1, rowsAffected)\n        assertNull(getStudent(jude!!.id.toLong()))\n        rowsAffected = rose!!.delete()\n        assertEquals(1, rowsAffected)\n        assertNull(getStudent(rose!!.id.toLong()))\n        rowsAffected = john!!.delete()\n        assertEquals(1, rowsAffected)\n        assertNull(getTeacher(john!!.id.toLong()))\n        rowsAffected = mike!!.delete()\n        assertEquals(1, rowsAffected)\n        assertNull(getTeacher(mike!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteCascadeM2OAssociationsOnOSideById() {\n        createClassroomStudentsTeachers()\n        var rowsAffected = LitePal.delete<Student>(jude!!.id.toLong())\n        assertEquals(1, rowsAffected)\n        assertNull(getStudent(jude!!.id.toLong()))\n        rowsAffected = LitePal.delete<Student>(rose!!.id.toLong())\n        assertEquals(1, rowsAffected)\n        assertNull(getStudent(rose!!.id.toLong()))\n        rowsAffected = LitePal.delete<Teacher>(john!!.id.toLong())\n        assertEquals(1, rowsAffected)\n        assertNull(getTeacher(john!!.id.toLong()))\n        rowsAffected = LitePal.delete<Teacher>(mike!!.id.toLong())\n        assertEquals(1, rowsAffected)\n        assertNull(getTeacher(mike!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteAllCascadeM2OAssociationsOnOSide() {\n        createClassroomStudentsTeachers()\n        var rowsAffected = LitePal.deleteAll<Student>(\"id = ?\", jude!!.id.toString())\n        assertEquals(1, rowsAffected)\n        assertNull(getStudent(jude!!.id.toLong()))\n        rowsAffected = LitePal.deleteAll<Student>(\"id = ?\", rose!!.id.toString())\n        assertEquals(1, rowsAffected)\n        assertNull(getStudent(rose!!.id.toLong()))\n        rowsAffected = LitePal.deleteAll<Teacher>(\"id = ?\", john!!.id.toString())\n        assertEquals(1, rowsAffected)\n        assertNull(getTeacher(john!!.id.toLong()))\n        rowsAffected = LitePal.deleteAll<Teacher>(\"id = ?\", mike!!.id.toString())\n        assertEquals(1, rowsAffected)\n        assertNull(getTeacher(mike!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteCascadeO2OAssociationsWithNoParameter() {\n        createStudentsTeachersWithIdCard()\n        var affectedRows = jude!!.delete()\n        assertEquals(2, affectedRows)\n        assertNull(getStudent(jude!!.id.toLong()))\n        assertNull(getIdCard(judeCard!!.id.toLong()))\n        affectedRows = roseCard!!.delete()\n        assertEquals(2, affectedRows)\n        assertNull(getStudent(rose!!.id.toLong()))\n        assertNull(getIdCard(roseCard!!.id.toLong()))\n        affectedRows = john!!.delete()\n        assertEquals(2, affectedRows)\n        assertNull(getTeacher(john!!.id.toLong()))\n        assertNull(getIdCard(johnCard!!.id.toLong()))\n        affectedRows = mikeCard!!.delete()\n        assertEquals(1, affectedRows)\n        assertNull(getIdCard(mikeCard!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteCascadeO2OAssociationsById() {\n        createStudentsTeachersWithIdCard()\n        var affectedRows = LitePal.delete<Student>(jude!!.id.toLong())\n        assertEquals(2, affectedRows)\n        assertNull(getStudent(jude!!.id.toLong()))\n        assertNull(getIdCard(judeCard!!.id.toLong()))\n        affectedRows = LitePal.delete<IdCard>(roseCard!!.id.toLong())\n        assertEquals(2, affectedRows)\n        assertNull(getStudent(rose!!.id.toLong()))\n        assertNull(getIdCard(roseCard!!.id.toLong()))\n        affectedRows = LitePal.delete<Teacher>(john!!.id.toLong())\n        assertEquals(2, affectedRows)\n        assertNull(getTeacher(john!!.id.toLong()))\n        assertNull(getIdCard(johnCard!!.id.toLong()))\n        affectedRows = LitePal.delete<IdCard>(mikeCard!!.id.toLong())\n        assertEquals(1, affectedRows)\n        assertNull(getIdCard(mikeCard!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteAllCascadeO2OAssociations() {\n        createStudentsTeachersWithIdCard()\n        var affectedRows = LitePal.deleteAll<Student>(\"id = ?\", jude!!.id.toString())\n        assertEquals(2, affectedRows)\n        assertNull(getStudent(jude!!.id.toLong()))\n        assertNull(getIdCard(judeCard!!.id.toLong()))\n        affectedRows = LitePal.deleteAll<IdCard>(\"id = ?\", roseCard!!.id.toString() + \"\")\n        assertEquals(2, affectedRows)\n        assertNull(getStudent(rose!!.id.toLong()))\n        assertNull(getIdCard(roseCard!!.id.toLong()))\n        affectedRows = LitePal.deleteAll<Teacher>(\"id = ?\", \"\" + john!!.id)\n        assertEquals(2, affectedRows)\n        assertNull(getTeacher(john!!.id.toLong()))\n        assertNull(getIdCard(johnCard!!.id.toLong()))\n        affectedRows = LitePal.deleteAll<IdCard>(\"id=?\", \"\" + mikeCard!!.id)\n        assertEquals(1, affectedRows)\n        assertNull(getIdCard(mikeCard!!.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteCascadeM2MAssociationsWithNoParameter() {\n        createStudentsTeachersWithAssociations()\n        var rowsAffected = jude!!.delete()\n        assertEquals(2, rowsAffected)\n        assertNull(getStudent(jude!!.id.toLong()))\n        assertM2MFalse(studentTable, teacherTable, jude!!.id.toLong(), mike!!.id.toLong())\n        assertM2M(studentTable, teacherTable, rose!!.id.toLong(), mike!!.id.toLong())\n        assertM2M(studentTable, teacherTable, rose!!.id.toLong(), john!!.id.toLong())\n        createStudentsTeachersWithAssociations()\n        rowsAffected = rose!!.delete()\n        assertEquals(3, rowsAffected)\n        assertNull(getStudent(rose!!.id.toLong()))\n        assertM2MFalse(studentTable, teacherTable, rose!!.id.toLong(), mike!!.id.toLong())\n        assertM2MFalse(studentTable, teacherTable, rose!!.id.toLong(), john!!.id.toLong())\n        assertM2M(studentTable, teacherTable, jude!!.id.toLong(), mike!!.id.toLong())\n    }\n\n    @Test\n    fun testDeleteCascadeM2MAssociationsById() {\n        createStudentsTeachersWithAssociations()\n        var rowsAffected = LitePal.delete<Teacher>(john!!.id.toLong())\n        assertEquals(2, rowsAffected)\n        assertNull(getTeacher(john!!.id.toLong()))\n        assertM2MFalse(studentTable, teacherTable, rose!!.id.toLong(), john!!.id.toLong())\n        assertM2M(studentTable, teacherTable, rose!!.id.toLong(), mike!!.id.toLong())\n        assertM2M(studentTable, teacherTable, jude!!.id.toLong(), mike!!.id.toLong())\n        createStudentsTeachersWithAssociations()\n        rowsAffected = LitePal.delete<Teacher>(mike!!.id.toLong())\n        assertEquals(3, rowsAffected)\n        assertNull(getTeacher(mike!!.id.toLong()))\n        assertM2MFalse(studentTable, teacherTable, rose!!.id.toLong(), mike!!.id.toLong())\n        assertM2MFalse(studentTable, teacherTable, jude!!.id.toLong(), mike!!.id.toLong())\n        assertM2M(studentTable, teacherTable, rose!!.id.toLong(), john!!.id.toLong())\n    }\n\n    @Test\n    fun testDeleteAllCascadeM2MAssociations() {\n        createStudentsTeachersWithAssociations()\n        var rowsAffected = LitePal.deleteAll<Teacher>(\"id=?\", \"\" + john!!.id)\n        assertEquals(2, rowsAffected)\n        assertNull(getTeacher(john!!.id.toLong()))\n        assertM2MFalse(studentTable, teacherTable, rose!!.id.toLong(), john!!.id.toLong())\n        assertM2M(studentTable, teacherTable, rose!!.id.toLong(), mike!!.id.toLong())\n        assertM2M(studentTable, teacherTable, jude!!.id.toLong(), mike!!.id.toLong())\n        createStudentsTeachersWithAssociations()\n        rowsAffected = LitePal.deleteAll<Teacher>(\"id=?\", \"\" + mike!!.id)\n        assertEquals(3, rowsAffected)\n        assertNull(getTeacher(mike!!.id.toLong()))\n        assertM2MFalse(studentTable, teacherTable, rose!!.id.toLong(), mike!!.id.toLong())\n        assertM2MFalse(studentTable, teacherTable, jude!!.id.toLong(), mike!!.id.toLong())\n        assertM2M(studentTable, teacherTable, rose!!.id.toLong(), john!!.id.toLong())\n    }\n\n    @Test\n    fun testDeleteAllCascadeWithConditions() {\n        val classroom = Classroom()\n        classroom.name = \"1\" + System.currentTimeMillis()\n        classroom.save()\n        val classroom2 = Classroom()\n        classroom2.name = \"2\" + System.currentTimeMillis()\n        classroom2.save()\n        val s1 = Student()\n        s1.classroom = classroom\n        s1.save()\n        val s2 = Student()\n        s2.classroom = classroom\n        s2.save()\n        val s3 = Student()\n        s3.classroom = classroom2\n        s3.save()\n        var rows = LitePal.deleteAll<Classroom>(\"name = ?\", classroom.name)\n        assertEquals(3, rows)\n        assertNull(getClassroom(classroom._id.toLong()))\n        assertNull(getStudent(s1.id.toLong()))\n        assertNull(getStudent(s2.id.toLong()))\n        assertNotNull(getClassroom(classroom2._id.toLong()))\n        assertNotNull(getStudent(s3.id.toLong()))\n        rows = LitePal.deleteAll<Classroom>(\"name = ?\", classroom2.name)\n        assertEquals(2, rows)\n        assertNull(getClassroom(classroom2._id.toLong()))\n        assertNull(getStudent(s3.id.toLong()))\n    }\n\n    @Test\n    fun testDeleteAll() {\n        var s: Student\n        val ids = IntArray(5)\n        for (i in 0..4) {\n            s = Student()\n            s.name = \"Dusting\"\n            s.age = i + 10086\n            s.save()\n            ids[i] = s.id\n        }\n        var affectedRows = LitePal.deleteAll<Student>(\"name = ? and age = ?\", \"Dusting\", \"10088\")\n        assertEquals(1, affectedRows)\n        assertNull(getStudent(ids[2].toLong()))\n        affectedRows = LitePal.deleteAll<Student>(\"name = ? and age > ? and age < ?\", \"Dusting\", \"10085\", \"10092\")\n        assertEquals(4, affectedRows)\n    }\n\n    @Test\n    fun testDeleteAllRows() {\n        createStudentsTeachersWithIdCard()\n        var rowsCount = getRowsCount(teacherTable)\n        var affectedRows = LitePal.deleteAll<Teacher>()\n        assertTrue(rowsCount <= affectedRows)\n        rowsCount = getRowsCount(studentTable)\n        affectedRows = LitePal.deleteAll<Student>()\n        assertTrue(rowsCount <= affectedRows)\n        rowsCount = getRowsCount(DBUtility.getTableNameByClassName(IdCard::class.java.name))\n        affectedRows = LitePal.deleteAll<IdCard>()\n        assertTrue(rowsCount <= affectedRows)\n        createStudentsTeachersWithAssociations()\n        rowsCount = getRowsCount(teacherTable)\n        affectedRows = LitePal.deleteAll<Teacher>()\n        assertTrue(rowsCount <= affectedRows)\n        rowsCount = getRowsCount(studentTable)\n        affectedRows = LitePal.deleteAll<Student>()\n        assertTrue(rowsCount <= affectedRows)\n        rowsCount = getRowsCount(DBUtility.getIntermediateTableName(studentTable, teacherTable))\n        affectedRows = LitePal.deleteAll(DBUtility.getIntermediateTableName(studentTable, teacherTable))\n        assertTrue(rowsCount <= affectedRows)\n    }\n\n    @Test\n    fun testMarkAsDeleted() {\n        val students = ArrayList<Student>()\n        for (i in 0..4) {\n            val s = Student()\n            s.name = \"Dusting\"\n            s.age = i + 10\n            students.add(s)\n        }\n        LitePal.saveAll(students)\n        var list = LitePal.where(\"name=?\", \"Dusting\").find<Student>()\n        assertTrue(list.size >= 5)\n        LitePal.deleteAll(Student::class.java, \"name=?\", \"Dusting\")\n        list = LitePal.where(\"name=?\", \"Dusting\").find()\n        assertEquals(0, list.size)\n        LitePal.saveAll(students)\n        list = LitePal.where(\"name=?\", \"Dusting\").find()\n        assertEquals(0, list.size)\n        LitePal.markAsDeleted(students)\n        LitePal.saveAll(students)\n        list = LitePal.where(\"name=?\", \"Dusting\").find()\n        assertEquals(5, list.size)\n    }\n\n    @Test\n    fun testDeleteAllWithWrongConditions() {\n        try {\n            LitePal.deleteAll<Student>(\"name = 'Dustin'\", \"aaa\")\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\"The parameters in conditions are incorrect.\", e.message)\n        }\n\n        try {\n            LitePal.deleteAll<Student>(null, null)\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\"The parameters in conditions are incorrect.\", e.message)\n        }\n\n        try {\n            LitePal.deleteAll<Student>(\"address = ?\", \"HK\")\n            fail()\n        } catch (e: SQLiteException) {\n        }\n\n    }\n\n    @Test\n    fun testDeleteWithGenericData() {\n        val classroom = Classroom()\n        classroom.name = \"classroom1\"\n        classroom.news.add(\"news1\")\n        classroom.news.add(\"news2\")\n        classroom.news.add(\"news3\")\n        classroom.save()\n        val id = classroom._id\n        val tableName = DBUtility.getGenericTableName(Classroom::class.java.name, \"news\")\n        val column = DBUtility.getGenericValueIdColumnName(Classroom::class.java.name)\n        var c = LitePal.findBySQL(\"select * from $tableName where $column = ?\", id.toString())\n        assertEquals(3, c!!.count)\n        c.close()\n        classroom.delete()\n        c = LitePal.findBySQL(\"select * from $tableName where $column = ?\", id.toString())\n        assertEquals(0, c!!.count)\n        c.close()\n        assertFalse(classroom.isSaved)\n        classroom.save()\n        assertTrue(classroom.isSaved)\n        c = LitePal.findBySQL(\"select * from $tableName where $column = ?\", classroom._id.toString())\n        assertEquals(3, c!!.count)\n        c.close()\n        LitePal.deleteAll<Classroom>(\"id = ?\", classroom._id.toString())\n        c = LitePal.findBySQL(\"select * from $tableName where $column = ?\", classroom._id.toString())\n        assertEquals(0, c!!.count)\n        c.close()\n    }\n\n    private fun initGameRoom() {\n        gameRoom = Classroom()\n        gameRoom!!.name = \"Game room\"\n    }\n\n    private fun initJude() {\n        jude = Student()\n        jude!!.name = \"Jude\"\n        jude!!.age = 13\n        judeCard = IdCard()\n        judeCard!!.address = \"Jude Street\"\n        judeCard!!.number = \"123456\"\n        jude!!.idcard = judeCard\n        judeCard!!.student = jude\n    }\n\n    private fun initRose() {\n        rose = Student()\n        rose!!.name = \"Rose\"\n        rose!!.age = 15\n        roseCard = IdCard()\n        roseCard!!.address = \"Rose Street\"\n        roseCard!!.number = \"123457\"\n        roseCard!!.student = rose\n    }\n\n    private fun initJohn() {\n        john = Teacher()\n        john!!.teacherName = \"John\"\n        john!!.age = 33\n        john!!.teachYears = 13\n        johnCard = IdCard()\n        johnCard!!.address = \"John Street\"\n        johnCard!!.number = \"123458\"\n        john!!.idCard = johnCard\n    }\n\n    private fun initMike() {\n        mike = Teacher()\n        mike!!.teacherName = \"Mike\"\n        mike!!.age = 36\n        mike!!.teachYears = 16\n        mikeCard = IdCard()\n        mikeCard!!.address = \"Mike Street\"\n        mikeCard!!.number = \"123459\"\n        mike!!.idCard = mikeCard\n    }\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/delete/DeleteTest.java",
    "content": "package com.litepaltest.test.crud.delete;\n\nimport android.database.Cursor;\n\nimport android.database.sqlite.SQLiteException;\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.IdCard;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.LitePal;\nimport org.litepal.exceptions.DataSupportException;\nimport org.litepal.util.DBUtility;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertNotNull;\nimport static junit.framework.TestCase.assertNull;\nimport static junit.framework.TestCase.assertTrue;\nimport static junit.framework.TestCase.fail;\n\n@SmallTest\npublic class DeleteTest extends LitePalTestCase {\n\n\tprivate Classroom gameRoom;\n\n\tprivate Student jude;\n\n\tprivate Student rose;\n\n\tprivate Teacher john;\n\n\tprivate Teacher mike;\n\n\tprivate IdCard judeCard;\n\n\tprivate IdCard roseCard;\n\n\tprivate IdCard johnCard;\n\n\tprivate IdCard mikeCard;\n    \n    private String studentTable;\n    \n    private String teacherTable;\n\n    @Before\n    public void setUp() {\n        studentTable = DBUtility.getTableNameByClassName(Student.class.getName());\n        teacherTable = DBUtility.getTableNameByClassName(Teacher.class.getName());\n    }\n\n    @Test\n    public void createClassroomStudentsTeachers() {\n\t\tinitGameRoom();\n\t\tinitRose();\n\t\tinitJude();\n\t\tinitMike();\n\t\tinitJohn();\n\t\tSet<Student> students = new HashSet<Student>();\n\t\tstudents.add(rose);\n\t\tstudents.add(jude);\n\t\tgameRoom.setStudentCollection(students);\n\t\tgameRoom.getTeachers().add(john);\n\t\tgameRoom.getTeachers().add(mike);\n\t\tgameRoom.save();\n\t\trose.save();\n\t\tjude.save();\n\t\tjohn.save();\n\t\tmike.save();\n\t}\n\n    @Test\n\tpublic void createStudentsTeachersWithIdCard() {\n\t\tinitRose();\n\t\tinitJude();\n\t\tinitMike();\n\t\tinitJohn();\n\t\trose.save();\n\t\tjude.save();\n\t\tmike.save();\n\t\tjohn.save();\n\t\troseCard.save();\n\t\tjudeCard.save();\n\t\tmikeCard.save();\n\t\tjohnCard.save();\n\t}\n\n    @Test\n\tpublic void createStudentsTeachersWithAssociations() {\n\t\tinitRose();\n\t\tinitJude();\n\t\tinitMike();\n\t\tinitJohn();\n\t\trose.getTeachers().add(john);\n\t\trose.getTeachers().add(mike);\n\t\tjude.getTeachers().add(mike);\n\t\trose.save();\n\t\tjude.save();\n\t\tjohn.save();\n\t\tmike.save();\n\t}\n\n    @Test\n\tpublic void testDeleteWithNoParameter() {\n\t\tinitJude();\n\t\tjude.save();\n\t\tint rowsAffected = jude.delete();\n\t\tassertEquals(1, rowsAffected);\n\t\tStudent s = getStudent(jude.getId());\n\t\tassertNull(s);\n\t}\n\n    @Test\n\tpublic void testDeleteById() {\n\t\tinitJude();\n\t\tjude.save();\n\t\tint rowsAffected = LitePal.delete(Student.class, jude.getId());\n\t\tassertEquals(1, rowsAffected);\n\t\tStudent s = getStudent(jude.getId());\n\t\tassertNull(s);\n\t}\n\n    @Test\n\tpublic void testDeleteNoSavedModelWithNoParameter() {\n\t\tStudent tony = new Student();\n\t\ttony.setName(\"Tony\");\n\t\ttony.setAge(23);\n\t\tint rowsAffected = tony.delete();\n\t\tassertEquals(0, rowsAffected);\n\t}\n\n    @Test\n\tpublic void testDeleteWithNotExistsRecordById() {\n\t\tint rowsAffected = LitePal.delete(Student.class, 998909);\n\t\tassertEquals(0, rowsAffected);\n\t}\n\n    @Test\n\tpublic void testDeleteCascadeM2OAssociationsOnMSideWithNoParameter() {\n\t\tcreateClassroomStudentsTeachers();\n\t\tint rowsAffected = gameRoom.delete();\n\t\tassertEquals(5, rowsAffected);\n\t\tassertNull(getClassroom(gameRoom.get_id()));\n\t\tassertNull(getStudent(jude.getId()));\n\t\tassertNull(getStudent(rose.getId()));\n\t\tassertNull(getTeacher(john.getId()));\n\t\tassertNull(getTeacher(mike.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteCascadeM2OAssociationsOnMSideById() {\n\t\tcreateClassroomStudentsTeachers();\n\t\tint rowsAffected = LitePal.delete(Classroom.class, gameRoom.get_id());\n\t\tassertEquals(5, rowsAffected);\n\t\tassertNull(getClassroom(gameRoom.get_id()));\n\t\tassertNull(getStudent(jude.getId()));\n\t\tassertNull(getStudent(rose.getId()));\n\t\tassertNull(getTeacher(john.getId()));\n\t\tassertNull(getTeacher(mike.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteAllCascadeM2OAssociationsOnMSide() {\n\t\tcreateClassroomStudentsTeachers();\n\t\tint rowsAffected = LitePal.deleteAll(Classroom.class, \"id = ?\", gameRoom.get_id() + \"\");\n\t\tassertEquals(5, rowsAffected);\n\t\tassertNull(getClassroom(gameRoom.get_id()));\n\t\tassertNull(getStudent(jude.getId()));\n\t\tassertNull(getStudent(rose.getId()));\n\t\tassertNull(getTeacher(john.getId()));\n\t\tassertNull(getTeacher(mike.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteCascadeM2OAssociationsOnOSideWithNoParameter() {\n\t\tcreateClassroomStudentsTeachers();\n\t\tint rowsAffected = jude.delete();\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getStudent(jude.getId()));\n\t\trowsAffected = rose.delete();\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getStudent(rose.getId()));\n\t\trowsAffected = john.delete();\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getTeacher(john.getId()));\n\t\trowsAffected = mike.delete();\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getTeacher(mike.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteCascadeM2OAssociationsOnOSideById() {\n\t\tcreateClassroomStudentsTeachers();\n\t\tint rowsAffected = LitePal.delete(Student.class, jude.getId());\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getStudent(jude.getId()));\n\t\trowsAffected = LitePal.delete(Student.class, rose.getId());\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getStudent(rose.getId()));\n\t\trowsAffected = LitePal.delete(Teacher.class, john.getId());\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getTeacher(john.getId()));\n\t\trowsAffected = LitePal.delete(Teacher.class, mike.getId());\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getTeacher(mike.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteAllCascadeM2OAssociationsOnOSide() {\n\t\tcreateClassroomStudentsTeachers();\n\t\tint rowsAffected = LitePal.deleteAll(Student.class, \"id = ?\", String.valueOf(jude.getId()));\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getStudent(jude.getId()));\n\t\trowsAffected = LitePal.deleteAll(Student.class, \"id = ?\", String.valueOf(rose.getId()));\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getStudent(rose.getId()));\n\t\trowsAffected = LitePal.deleteAll(Teacher.class, \"id = ?\", String.valueOf(john.getId()));\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getTeacher(john.getId()));\n\t\trowsAffected = LitePal.deleteAll(Teacher.class, \"id = ?\", String.valueOf(mike.getId()));\n\t\tassertEquals(1, rowsAffected);\n\t\tassertNull(getTeacher(mike.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteCascadeO2OAssociationsWithNoParameter() {\n\t\tcreateStudentsTeachersWithIdCard();\n\t\tint affectedRows = jude.delete();\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getStudent(jude.getId()));\n\t\tassertNull(getIdCard(judeCard.getId()));\n\t\taffectedRows = roseCard.delete();\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getStudent(rose.getId()));\n\t\tassertNull(getIdCard(roseCard.getId()));\n\t\taffectedRows = john.delete();\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getTeacher(john.getId()));\n\t\tassertNull(getIdCard(johnCard.getId()));\n\t\taffectedRows = mikeCard.delete();\n\t\tassertEquals(1, affectedRows);\n\t\tassertNull(getIdCard(mikeCard.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteCascadeO2OAssociationsById() {\n\t\tcreateStudentsTeachersWithIdCard();\n\t\tint affectedRows = LitePal.delete(Student.class, jude.getId());\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getStudent(jude.getId()));\n\t\tassertNull(getIdCard(judeCard.getId()));\n\t\taffectedRows = LitePal.delete(IdCard.class, roseCard.getId());\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getStudent(rose.getId()));\n\t\tassertNull(getIdCard(roseCard.getId()));\n\t\taffectedRows = LitePal.delete(Teacher.class, john.getId());\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getTeacher(john.getId()));\n\t\tassertNull(getIdCard(johnCard.getId()));\n\t\taffectedRows = LitePal.delete(IdCard.class, mikeCard.getId());\n\t\tassertEquals(1, affectedRows);\n\t\tassertNull(getIdCard(mikeCard.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteAllCascadeO2OAssociations() {\n\t\tcreateStudentsTeachersWithIdCard();\n\t\tint affectedRows = LitePal.deleteAll(Student.class, \"id = ?\", String.valueOf(jude.getId()));\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getStudent(jude.getId()));\n\t\tassertNull(getIdCard(judeCard.getId()));\n\t\taffectedRows = LitePal.deleteAll(IdCard.class, \"id = ?\", roseCard.getId() + \"\");\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getStudent(rose.getId()));\n\t\tassertNull(getIdCard(roseCard.getId()));\n\t\taffectedRows = LitePal.deleteAll(Teacher.class, \"id = ?\", \"\" + john.getId());\n\t\tassertEquals(2, affectedRows);\n\t\tassertNull(getTeacher(john.getId()));\n\t\tassertNull(getIdCard(johnCard.getId()));\n\t\taffectedRows = LitePal.deleteAll(IdCard.class, \"id=?\", \"\" + mikeCard.getId());\n\t\tassertEquals(1, affectedRows);\n\t\tassertNull(getIdCard(mikeCard.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteCascadeM2MAssociationsWithNoParameter() {\n\t\tcreateStudentsTeachersWithAssociations();\n\t\tint rowsAffected = jude.delete();\n\t\tassertEquals(2, rowsAffected);\n\t\tassertNull(getStudent(jude.getId()));\n\t\tassertM2MFalse(studentTable, teacherTable, jude.getId(), mike.getId());\n\t\tassertM2M(studentTable, teacherTable, rose.getId(), mike.getId());\n\t\tassertM2M(studentTable, teacherTable, rose.getId(), john.getId());\n\t\tcreateStudentsTeachersWithAssociations();\n\t\trowsAffected = rose.delete();\n\t\tassertEquals(3, rowsAffected);\n\t\tassertNull(getStudent(rose.getId()));\n\t\tassertM2MFalse(studentTable, teacherTable, rose.getId(), mike.getId());\n\t\tassertM2MFalse(studentTable, teacherTable, rose.getId(), john.getId());\n\t\tassertM2M(studentTable, teacherTable, jude.getId(), mike.getId());\n\t}\n\n    @Test\n\tpublic void testDeleteCascadeM2MAssociationsById() {\n\t\tcreateStudentsTeachersWithAssociations();\n\t\tint rowsAffected = LitePal.delete(Teacher.class, john.getId());\n\t\tassertEquals(2, rowsAffected);\n\t\tassertNull(getTeacher(john.getId()));\n\t\tassertM2MFalse(studentTable, teacherTable, rose.getId(), john.getId());\n\t\tassertM2M(studentTable, teacherTable, rose.getId(), mike.getId());\n\t\tassertM2M(studentTable, teacherTable, jude.getId(), mike.getId());\n\t\tcreateStudentsTeachersWithAssociations();\n\t\trowsAffected = LitePal.delete(Teacher.class, mike.getId());\n\t\tassertEquals(3, rowsAffected);\n\t\tassertNull(getTeacher(mike.getId()));\n\t\tassertM2MFalse(studentTable, teacherTable, rose.getId(), mike.getId());\n\t\tassertM2MFalse(studentTable, teacherTable, jude.getId(), mike.getId());\n\t\tassertM2M(studentTable, teacherTable, rose.getId(), john.getId());\n\t}\n\n    @Test\n\tpublic void testDeleteAllCascadeM2MAssociations() {\n\t\tcreateStudentsTeachersWithAssociations();\n\t\tint rowsAffected = LitePal.deleteAll(Teacher.class, \"id=?\", \"\" + john.getId());\n\t\tassertEquals(2, rowsAffected);\n\t\tassertNull(getTeacher(john.getId()));\n\t\tassertM2MFalse(studentTable, teacherTable, rose.getId(), john.getId());\n\t\tassertM2M(studentTable, teacherTable, rose.getId(), mike.getId());\n\t\tassertM2M(studentTable, teacherTable, jude.getId(), mike.getId());\n\t\tcreateStudentsTeachersWithAssociations();\n\t\trowsAffected = LitePal.deleteAll(Teacher.class, \"id=?\", \"\" + mike.getId());\n\t\tassertEquals(3, rowsAffected);\n\t\tassertNull(getTeacher(mike.getId()));\n\t\tassertM2MFalse(studentTable, teacherTable, rose.getId(), mike.getId());\n\t\tassertM2MFalse(studentTable, teacherTable, jude.getId(), mike.getId());\n\t\tassertM2M(studentTable, teacherTable, rose.getId(), john.getId());\n\t}\n\n    @Test\n\tpublic void testDeleteAllCascadeWithConditions() {\n\t\tClassroom classroom = new Classroom();\n\t\tclassroom.setName(\"1\"+System.currentTimeMillis());\n\t\tclassroom.save();\n\t\tClassroom classroom2 = new Classroom();\n\t\tclassroom2.setName(\"2\"+ System.currentTimeMillis());\n\t\tclassroom2.save();\n\t\tStudent s1 = new Student();\n\t\ts1.setClassroom(classroom);\n\t\ts1.save();\n\t\tStudent s2 = new Student();\n\t\ts2.setClassroom(classroom);\n\t\ts2.save();\n\t\tStudent s3 = new Student();\n\t\ts3.setClassroom(classroom2);\n\t\ts3.save();\n\t\tint rows = LitePal.deleteAll(Classroom.class, \"name = ?\", classroom.getName());\n\t\tassertEquals(3, rows);\n\t\tassertNull(getClassroom(classroom.get_id()));\n\t\tassertNull(getStudent(s1.getId()));\n\t\tassertNull(getStudent(s2.getId()));\n\t\tassertNotNull(getClassroom(classroom2.get_id()));\n\t\tassertNotNull(getStudent(s3.getId()));\n\t\trows = LitePal.deleteAll(Classroom.class, \"name = ?\", classroom2.getName());\n\t\tassertEquals(2, rows);\n\t\tassertNull(getClassroom(classroom2.get_id()));\n\t\tassertNull(getStudent(s3.getId()));\n\t}\n\n    @Test\n\tpublic void testDeleteAll() {\n\t\tStudent s;\n\t\tint[] ids = new int[5];\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\ts = new Student();\n\t\t\ts.setName(\"Dusting\");\n\t\t\ts.setAge(i + 10086);\n\t\t\ts.save();\n\t\t\tids[i] = s.getId();\n\t\t}\n\t\tint affectedRows = LitePal.deleteAll(Student.class, \"name = ? and age = ?\", \"Dusting\",\n\t\t\t\t\"10088\");\n\t\tassertEquals(1, affectedRows);\n\t\tassertNull(getStudent(ids[2]));\n\t\taffectedRows = LitePal.deleteAll(Student.class, \"name = ? and age > ? and age < ?\", \"Dusting\", \"10085\", \"10092\");\n\t\tassertEquals(4, affectedRows);\n\t}\n\n    @Test\n\tpublic void testDeleteAllRows() {\n\t\tcreateStudentsTeachersWithIdCard();\n\t\tint rowsCount = getRowsCount(teacherTable);\n\t\tint affectedRows = 0;\n\t\taffectedRows = LitePal.deleteAll(Teacher.class);\n\t\tassertTrue(rowsCount <= affectedRows);\n\t\trowsCount = getRowsCount(studentTable);\n\t\taffectedRows = LitePal.deleteAll(Student.class);\n\t\tassertTrue(rowsCount<= affectedRows);\n\t\trowsCount = getRowsCount(DBUtility.getTableNameByClassName(IdCard.class.getName()));\n\t\taffectedRows = LitePal.deleteAll(IdCard.class);\n\t\tassertTrue(rowsCount<=affectedRows);\n\t\tcreateStudentsTeachersWithAssociations();\n\t\trowsCount = getRowsCount(teacherTable);\n\t\taffectedRows = LitePal.deleteAll(Teacher.class);\n\t\tassertTrue(rowsCount<=affectedRows);\n\t\trowsCount = getRowsCount(studentTable);\n\t\taffectedRows = LitePal.deleteAll(Student.class);\n\t\tassertTrue(rowsCount<=affectedRows);\n\t\trowsCount = getRowsCount(DBUtility.getIntermediateTableName(studentTable, teacherTable));\n\t\taffectedRows = LitePal.deleteAll(DBUtility.getIntermediateTableName(studentTable, teacherTable));\n\t\tassertTrue(rowsCount<=affectedRows);\n\t}\n\n    @Test\n    public void testMarkAsDeleted() {\n        List<Student> students = new ArrayList<>();\n        for (int i = 0; i < 5; i++) {\n            Student s = new Student();\n            s.setName(\"Dusting\");\n            s.setAge(i + 10);\n            students.add(s);\n        }\n        LitePal.saveAll(students);\n        List<Student> list = LitePal.where(\"name=?\", \"Dusting\").find(Student.class);\n        assertTrue(list.size() >= 5);\n        LitePal.deleteAll(Student.class, \"name=?\", \"Dusting\");\n        list = LitePal.where(\"name=?\", \"Dusting\").find(Student.class);\n        assertEquals(0, list.size());\n        LitePal.saveAll(students);\n        list = LitePal.where(\"name=?\", \"Dusting\").find(Student.class);\n        assertEquals(0, list.size());\n        LitePal.markAsDeleted(students);\n        LitePal.saveAll(students);\n        list = LitePal.where(\"name=?\", \"Dusting\").find(Student.class);\n        assertEquals(5, list.size());\n    }\n\n    @Test\n\tpublic void testDeleteAllWithWrongConditions() {\n\t\ttry {\n            LitePal.deleteAll(Student.class, \"name = 'Dustin'\", \"aaa\");\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\"The parameters in conditions are incorrect.\", e.getMessage());\n\t\t}\n\t\ttry {\n            LitePal.deleteAll(Student.class, null, null);\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\"The parameters in conditions are incorrect.\", e.getMessage());\n\t\t}\n\t\ttry {\n            LitePal.deleteAll(Student.class, \"address = ?\", \"HK\");\n\t\t\tfail();\n\t\t} catch (SQLiteException e) {\n\t\t}\n\t}\n\n    @Test\n    public void testDeleteWithGenericData() {\n        Classroom classroom = new Classroom();\n        classroom.setName(\"classroom1\");\n        classroom.getNews().add(\"news1\");\n        classroom.getNews().add(\"news2\");\n        classroom.getNews().add(\"news3\");\n        classroom.save();\n        int id = classroom.get_id();\n        String tableName = DBUtility.getGenericTableName(Classroom.class.getName(), \"news\");\n        String column = DBUtility.getGenericValueIdColumnName(Classroom.class.getName());\n        Cursor c = LitePal.findBySQL(\"select * from \" + tableName + \" where \" + column + \" = ?\", String.valueOf(id));\n        assertEquals(3, c.getCount());\n        c.close();\n        classroom.delete();\n        c = LitePal.findBySQL(\"select * from \" + tableName + \" where \" + column + \" = ?\", String.valueOf(id));\n        assertEquals(0, c.getCount());\n        c.close();\n        assertFalse(classroom.isSaved());\n        classroom.save();\n        assertTrue(classroom.isSaved());\n        c = LitePal.findBySQL(\"select * from \" + tableName + \" where \" + column + \" = ?\", String.valueOf(classroom.get_id()));\n        assertEquals(3, c.getCount());\n        c.close();\n        LitePal.deleteAll(Classroom.class, \"id = ?\", String.valueOf(classroom.get_id()));\n        c = LitePal.findBySQL(\"select * from \" + tableName + \" where \" + column + \" = ?\", String.valueOf(classroom.get_id()));\n        assertEquals(0, c.getCount());\n        c.close();\n    }\n\n    private void initGameRoom() {\n\t\tgameRoom = new Classroom();\n\t\tgameRoom.setName(\"Game room\");\n\t}\n\n\tprivate void initJude() {\n\t\tjude = new Student();\n\t\tjude.setName(\"Jude\");\n\t\tjude.setAge(13);\n\t\tjudeCard = new IdCard();\n\t\tjudeCard.setAddress(\"Jude Street\");\n\t\tjudeCard.setNumber(\"123456\");\n\t\tjude.setIdcard(judeCard);\n\t\tjudeCard.setStudent(jude);\n\t}\n\n\tprivate void initRose() {\n\t\trose = new Student();\n\t\trose.setName(\"Rose\");\n\t\trose.setAge(15);\n\t\troseCard = new IdCard();\n\t\troseCard.setAddress(\"Rose Street\");\n\t\troseCard.setNumber(\"123457\");\n\t\troseCard.setStudent(rose);\n\t}\n\n\tprivate void initJohn() {\n\t\tjohn = new Teacher();\n\t\tjohn.setTeacherName(\"John\");\n\t\tjohn.setAge(33);\n\t\tjohn.setTeachYears(13);\n\t\tjohnCard = new IdCard();\n\t\tjohnCard.setAddress(\"John Street\");\n\t\tjohnCard.setNumber(\"123458\");\n\t\tjohn.setIdCard(johnCard);\n\t}\n\n\tprivate void initMike() {\n\t\tmike = new Teacher();\n\t\tmike.setTeacherName(\"Mike\");\n\t\tmike.setAge(36);\n\t\tmike.setTeachYears(16);\n\t\tmikeCard = new IdCard();\n\t\tmikeCard.setAddress(\"Mike Street\");\n\t\tmikeCard.setNumber(\"123459\");\n\t\tmike.setIdCard(mikeCard);\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryBasicKotlinTest.kt",
    "content": "package com.litepaltest.test.crud.query\n\nimport androidx.test.filters.SmallTest\nimport com.litepaltest.model.Book\nimport com.litepaltest.test.LitePalTestCase\nimport junit.framework.TestCase.*\nimport org.junit.Test\nimport org.litepal.LitePal\nimport org.litepal.extension.*\n\n@SmallTest\nclass QueryBasicKotlinTest : LitePalTestCase() {\n\n    @Test\n    fun testFind() {\n        val isbn: Short = 30013\n        val book = Book()\n        book.area = 10.5f\n        book.bookName = \"Android Second Line\"\n        book.isbn = isbn\n        book.level = 'A'\n        book.pages = 450\n        book.price = 49.99\n        book.isPublished = false\n        book.save()\n        val b: Book? = LitePal.find(book.id)\n        assertNotNull(b)\n        assertEquals(book.id, b!!.id)\n        assertEquals(10.5f, b.area)\n        assertEquals(\"Android Second Line\", b.bookName)\n        assertEquals(isbn, b.isbn)\n        assertEquals('A', b.level)\n        assertTrue(450 == b.pages)\n        assertEquals(49.99, b.price)\n        assertFalse(b.isPublished)\n        assertTrue(b.isSaved)\n    }\n\n    @Test\n    fun testFindMul() {\n        val isbn1: Short = 30017\n        val book1 = Book()\n        book1.area = 1.5f\n        book1.bookName = \"Android Second Line\"\n        book1.isbn = isbn1\n        book1.level = 'B'\n        book1.pages = 434\n        book1.price = 40.99\n        book1.isPublished = true\n        book1.save()\n        val isbn2: Short = 30014\n        val book2 = Book()\n        book2.area = 8.8f\n        book2.bookName = \"Android Third Line\"\n        book2.isbn = isbn2\n        book2.level = 'C'\n        book2.pages = 411\n        book2.price = 35.99\n        book2.isPublished = false\n        book2.save()\n        val bookList = LitePal.findAll<Book>(book1.id, book2.id)\n        assertEquals(2, bookList.size)\n        for (book in bookList) {\n            if (book.id == book1.id) {\n                assertEquals(1.5f, book.area)\n                assertEquals(\"Android Second Line\", book.bookName)\n                assertEquals(isbn1, book.isbn)\n                assertEquals('B', book.level)\n                assertTrue(434 == book.pages)\n                assertEquals(40.99, book.price)\n                assertTrue(book.isPublished)\n                assertTrue(book.isSaved)\n                continue\n            } else if (book.id == book2.id) {\n                assertEquals(8.8f, book.area)\n                assertEquals(\"Android Third Line\", book.bookName)\n                assertEquals(isbn2, book.isbn)\n                assertEquals('C', book.level)\n                assertTrue(411 == book.pages)\n                assertEquals(35.99, book.price)\n                assertFalse(book.isPublished)\n                assertTrue(book.isSaved)\n                continue\n            }\n            fail()\n        }\n    }\n\n    @Test\n    fun testFindAll() {\n        val expectBooks = getBooks(null, null, null, null, null, null, null)\n        val realBooks = LitePal.findAll<Book>()\n        assertEquals(expectBooks.size, realBooks.size)\n        for (i in expectBooks.indices) {\n            val expectBook = expectBooks[i]\n            val realBook = realBooks[i]\n            assertEquals(expectBook.id, realBook.id)\n            assertEquals(expectBook.bookName, realBook.bookName)\n            assertEquals(expectBook.pages, realBook.pages)\n            assertEquals(expectBook.price, realBook.price)\n            assertEquals(expectBook.area, realBook.area)\n            assertEquals(expectBook.isbn, realBook.isbn)\n            assertEquals(expectBook.level, realBook.level)\n            assertEquals(expectBook.isPublished, realBook.isPublished)\n            assertTrue(realBook.isSaved)\n        }\n    }\n\n    @Test\n    fun testFindFirst() {\n        val expectedBooks = getBooks(null, null, null, null, null, null, null)\n        val expectedFirstBook = expectedBooks[0]\n        val realFirstBook = LitePal.findFirst<Book>()\n        assertEquals(expectedFirstBook.id, realFirstBook!!.id)\n        assertEquals(expectedFirstBook.bookName, realFirstBook.bookName)\n        assertEquals(expectedFirstBook.pages, realFirstBook.pages)\n        assertEquals(expectedFirstBook.price, realFirstBook.price)\n        assertEquals(expectedFirstBook.area, realFirstBook.area)\n        assertEquals(expectedFirstBook.isbn, realFirstBook.isbn)\n        assertEquals(expectedFirstBook.level, realFirstBook.level)\n        assertEquals(expectedFirstBook.isPublished, realFirstBook.isPublished)\n        assertTrue(realFirstBook.isSaved)\n    }\n\n    @Test\n    fun testFindLast() {\n        val expectedBooks = getBooks(null, null, null, null, null, null, null)\n        val expectedLastBook = expectedBooks[expectedBooks.size - 1]\n        val realLastBook = LitePal.findLast<Book>()\n        assertEquals(expectedLastBook.id, realLastBook!!.id)\n        assertEquals(expectedLastBook.bookName, realLastBook.bookName)\n        assertEquals(expectedLastBook.pages, realLastBook.pages)\n        assertEquals(expectedLastBook.price, realLastBook.price)\n        assertEquals(expectedLastBook.area, realLastBook.area)\n        assertEquals(expectedLastBook.isbn, realLastBook.isbn)\n        assertEquals(expectedLastBook.level, realLastBook.level)\n        assertEquals(expectedLastBook.isPublished, realLastBook.isPublished)\n        assertTrue(realLastBook.isSaved)\n    }\n\n    @Test\n    fun testIsExist() {\n        val book = Book()\n        book.area = 10.5f\n        book.bookName = \"Android Third Line\"\n        book.pages = 556\n        book.price = 49.99\n        book.isPublished = false\n        book.save()\n\n        val book2 = Book()\n        book2.area = 10.5f\n        book2.bookName = \"Android Fourth Line\"\n        book2.pages = 818\n        book2.price = 59.99\n        book2.isPublished = false\n        book2.save()\n\n        assertTrue(LitePal.isExist<Book>(\"bookname = ? and pages = ?\", \"Android Third Line\", \"556\"))\n        assertFalse(LitePal.isExist<Book>(\"bookname = ? and pages = ?\", \"Android Third Lines\", \"556\"))\n        assertTrue(LitePal.isExist<Book>(\"bookname = ? and pages = ?\", \"Android Fourth Line\", \"818\"))\n        assertFalse(LitePal.isExist<Book>(\"bookname = ? and pages = ?\", \"Android Fourth Line\", \"813\"))\n    }\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryBasicTest.java",
    "content": "package com.litepaltest.test.crud.query;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Book;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Test;\nimport org.litepal.LitePal;\n\nimport java.util.List;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertTrue;\nimport static junit.framework.TestCase.fail;\n\n@SmallTest\npublic class QueryBasicTest extends LitePalTestCase {\n\n    @Test\n\tpublic void testFind() {\n\t\tshort isbn = 30013;\n\t\tBook book = new Book();\n\t\tbook.setArea(10.5f);\n\t\tbook.setBookName(\"Android First Line\");\n\t\tbook.setIsbn(isbn);\n\t\tbook.setLevel('A');\n\t\tbook.setPages(450);\n\t\tbook.setPrice(49.99);\n\t\tbook.setPublished(false);\n\t\tbook.save();\n\t\tBook b = LitePal.find(Book.class, book.getId());\n\t\tassertEquals(book.getId(), b.getId());\n\t\tassertEquals(10.5f, b.getArea());\n\t\tassertEquals(\"Android First Line\", b.getBookName());\n\t\tassertEquals(isbn, b.getIsbn());\n\t\tassertEquals('A', b.getLevel());\n\t\tassertTrue(450 == b.getPages());\n\t\tassertEquals(49.99, b.getPrice());\n\t\tassertFalse(b.isPublished());\n\t\tassertTrue(b.isSaved());\n\t}\n\n    @Test\n\tpublic void testFindMul() {\n\t\tshort isbn1 = 30017;\n\t\tBook book1 = new Book();\n\t\tbook1.setArea(1.5f);\n\t\tbook1.setBookName(\"Android Second Line\");\n\t\tbook1.setIsbn(isbn1);\n\t\tbook1.setLevel('B');\n\t\tbook1.setPages(434);\n\t\tbook1.setPrice(40.99);\n\t\tbook1.setPublished(true);\n\t\tbook1.save();\n\t\tshort isbn2 = 30014;\n\t\tBook book2 = new Book();\n\t\tbook2.setArea(8.8f);\n\t\tbook2.setBookName(\"Android Third Line\");\n\t\tbook2.setIsbn(isbn2);\n\t\tbook2.setLevel('C');\n\t\tbook2.setPages(411);\n\t\tbook2.setPrice(35.99);\n\t\tbook2.setPublished(false);\n\t\tbook2.save();\n\t\tList<Book> bookList = LitePal.findAll(Book.class, book1.getId(), book2.getId());\n\t\tassertEquals(2, bookList.size());\n\t\tfor (Book book : bookList) {\n\t\t\tif (book.getId() == book1.getId()) {\n\t\t\t\tassertEquals(1.5f, book.getArea());\n\t\t\t\tassertEquals(\"Android Second Line\", book.getBookName());\n\t\t\t\tassertEquals(isbn1, book.getIsbn());\n\t\t\t\tassertEquals('B', book.getLevel());\n\t\t\t\tassertTrue(434 == book.getPages());\n\t\t\t\tassertEquals(40.99, book.getPrice());\n\t\t\t\tassertTrue(book.isPublished());\n\t\t\t\tassertTrue(book.isSaved());\n\t\t\t\tcontinue;\n\t\t\t} else if (book.getId() == book2.getId()) {\n\t\t\t\tassertEquals(8.8f, book.getArea());\n\t\t\t\tassertEquals(\"Android Third Line\", book.getBookName());\n\t\t\t\tassertEquals(isbn2, book.getIsbn());\n\t\t\t\tassertEquals('C', book.getLevel());\n\t\t\t\tassertTrue(411 == book.getPages());\n\t\t\t\tassertEquals(35.99, book.getPrice());\n\t\t\t\tassertFalse(book.isPublished());\n\t\t\t\tassertTrue(book.isSaved());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\t}\n\n    @Test\n\tpublic void testFindAll() {\n\t\tList<Book> expectBooks = getBooks(null, null, null, null, null, null, null);\n\t\tList<Book> realBooks = LitePal.findAll(Book.class);\n\t\tassertEquals(expectBooks.size(), realBooks.size());\n\t\tfor (int i = 0; i < expectBooks.size(); i++) {\n\t\t\tBook expectBook = expectBooks.get(i);\n\t\t\tBook realBook = realBooks.get(i);\n\t\t\tassertEquals(expectBook.getId(), realBook.getId());\n\t\t\tassertEquals(expectBook.getBookName(), realBook.getBookName());\n\t\t\tassertEquals(expectBook.getPages(), realBook.getPages());\n\t\t\tassertEquals(expectBook.getPrice(), realBook.getPrice());\n\t\t\tassertEquals(expectBook.getArea(), realBook.getArea());\n\t\t\tassertEquals(expectBook.getIsbn(), realBook.getIsbn());\n\t\t\tassertEquals(expectBook.getLevel(), realBook.getLevel());\n\t\t\tassertEquals(expectBook.isPublished(), realBook.isPublished());\n\t\t\tassertTrue(realBook.isSaved());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testFindFirst() {\n\t\tList<Book> expectedBooks = getBooks(null, null, null, null, null, null, null);\n\t\tBook expectedFirstBook = expectedBooks.get(0);\n\t\tBook realFirstBook = LitePal.findFirst(Book.class);\n\t\tassertEquals(expectedFirstBook.getId(), realFirstBook.getId());\n\t\tassertEquals(expectedFirstBook.getBookName(), realFirstBook.getBookName());\n\t\tassertEquals(expectedFirstBook.getPages(), realFirstBook.getPages());\n\t\tassertEquals(expectedFirstBook.getPrice(), realFirstBook.getPrice());\n\t\tassertEquals(expectedFirstBook.getArea(), realFirstBook.getArea());\n\t\tassertEquals(expectedFirstBook.getIsbn(), realFirstBook.getIsbn());\n\t\tassertEquals(expectedFirstBook.getLevel(), realFirstBook.getLevel());\n\t\tassertEquals(expectedFirstBook.isPublished(), realFirstBook.isPublished());\n\t\tassertTrue(realFirstBook.isSaved());\n\t}\n\n    @Test\n\tpublic void testFindLast() {\n\t\tList<Book> expectedBooks = getBooks(null, null, null, null, null, null, null);\n\t\tBook expectedLastBook = expectedBooks.get(expectedBooks.size() - 1);\n\t\tBook realLastBook = LitePal.findLast(Book.class);\n\t\tassertEquals(expectedLastBook.getId(), realLastBook.getId());\n\t\tassertEquals(expectedLastBook.getBookName(), realLastBook.getBookName());\n\t\tassertEquals(expectedLastBook.getPages(), realLastBook.getPages());\n\t\tassertEquals(expectedLastBook.getPrice(), realLastBook.getPrice());\n\t\tassertEquals(expectedLastBook.getArea(), realLastBook.getArea());\n\t\tassertEquals(expectedLastBook.getIsbn(), realLastBook.getIsbn());\n\t\tassertEquals(expectedLastBook.getLevel(), realLastBook.getLevel());\n\t\tassertEquals(expectedLastBook.isPublished(), realLastBook.isPublished());\n\t\tassertTrue(realLastBook.isSaved());\n\t}\n\n    @Test\n    public void testIsExist() {\n        Book book = new Book();\n        book.setArea(10.5f);\n        book.setBookName(\"Android Third Line\");\n        book.setPages(556);\n        book.setPrice(49.99);\n        book.setPublished(false);\n        book.save();\n\n        Book book2 = new Book();\n        book2.setArea(10.5f);\n        book2.setBookName(\"Android Fourth Line\");\n        book2.setPages(818);\n        book2.setPrice(59.99);\n        book2.setPublished(false);\n        book2.save();\n\n        assertTrue(LitePal.isExist(Book.class, \"bookname = ? and pages = ?\", \"Android Third Line\", \"556\"));\n        assertFalse(LitePal.isExist(Book.class, \"bookname = ? and pages = ?\", \"Android Third Lines\", \"556\"));\n        assertTrue(LitePal.isExist(Book.class, \"bookname = ? and pages = ?\", \"Android Fourth Line\", \"818\"));\n        assertFalse(LitePal.isExist(Book.class, \"bookname = ? and pages = ?\", \"Android Fourth Line\", \"813\"));\n    }\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryBySQLTest.java",
    "content": "package com.litepaltest.test.crud.query;\n\nimport android.database.Cursor;\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Book;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.LitePal;\nimport org.litepal.exceptions.DataSupportException;\nimport org.litepal.util.DBUtility;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertNull;\nimport static junit.framework.TestCase.assertTrue;\nimport static junit.framework.TestCase.fail;\n\n@SmallTest\npublic class QueryBySQLTest {\n\n\tprivate Book book;\n\n    private String bookTable;\n\n\t@Before\n\tpublic void setUp() {\n        bookTable = DBUtility.getTableNameByClassName(Book.class.getName());\n\t\tbook = new Book();\n\t\tbook.setBookName(\"数据库\");\n\t\tbook.setPages(300);\n\t\tbook.save();\n\t}\n\n\t@Test\n\tpublic void testQueryBySQL() {\n\t\tCursor cursor = LitePal.findBySQL(\"select * from \" + bookTable);\n\t\tassertTrue(cursor.getCount() > 0);\n\t\tcursor.close();\n\t}\n\n    @Test\n\tpublic void testQueryBySQLWithPlaceHolder() {\n\t\tCursor cursor = LitePal.findBySQL(\n\t\t\t\t\"select * from \" + bookTable + \" where id=? and bookname=? and pages=?\",\n\t\t\t\tString.valueOf(book.getId()), \"数据库\", \"300\");\n\t\tassertEquals(1, cursor.getCount());\n\t\tcursor.moveToFirst();\n\t\tString bookName = cursor.getString(cursor.getColumnIndexOrThrow(\"bookname\"));\n\t\tint pages = cursor.getInt(cursor.getColumnIndexOrThrow(\"pages\"));\n\t\tassertEquals(bookName, \"数据库\");\n\t\tassertEquals(pages, 300);\n\t\tcursor.close();\n\t}\n\n    @Test\n\tpublic void testQueryBySQLWithWrongParams() {\n\t\ttry {\n            LitePal.findBySQL(\"select * from \" + bookTable + \" where id=? and bookname=? and pages=?\",\n\t\t\t\t\tString.valueOf(book.getId()), \"数据库\");\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\"The parameters in conditions are incorrect.\", e.getMessage());\n\t\t}\n\t\tCursor cursor = LitePal.findBySQL();\n\t\tassertNull(cursor);\n\t\tcursor = LitePal.findBySQL();\n\t\tassertNull(cursor);\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryClusterKotlinTest.kt",
    "content": "package com.litepaltest.test.crud.query\n\nimport androidx.test.filters.SmallTest\nimport com.litepaltest.model.Book\nimport com.litepaltest.test.LitePalTestCase\nimport junit.framework.TestCase.*\nimport org.junit.Test\nimport org.litepal.LitePal\nimport org.litepal.extension.*\n\n@SmallTest\nclass QueryClusterKotlinTest : LitePalTestCase() {\n\n    @Test\n    fun testSelect() {\n        val expectedBooks = getBooks(null, null, null, null, null, null, null)\n        val books = LitePal.select(\"bookname\", \"price\").find<Book>()\n        assertEquals(expectedBooks.size, books.size)\n        val firstBook = LitePal.select(\"bookname\", \"price\").findFirst<Book>()\n        val lastBook = LitePal.select(\"bookname\", \"price\").findLast<Book>()\n        assertNotNull(firstBook)\n        for (i in books.indices) {\n            val book = books[i]\n            assertTrue(book.isSaved)\n            assertEquals(expectedBooks[i].bookName, book.bookName)\n            assertNull(book.pages)\n            assertEquals(false, book.isPublished)\n            assertEquals(0f, book.area)\n            assertEquals(expectedBooks[i].price, book.price)\n            assertEquals(0, book.isbn.toInt())\n            assertEquals(0, book.level.toInt())\n            assertEquals(expectedBooks[i].id, book.id)\n            if (i == 0) {\n                assertEquals(firstBook!!.isSaved, book.isSaved)\n                assertEquals(firstBook.bookName, book.bookName)\n                assertNull(firstBook.pages)\n                assertEquals(firstBook.isPublished, book.isPublished)\n                assertEquals(firstBook.price, book.price)\n                assertEquals(firstBook.area, book.area)\n                assertEquals(firstBook.isbn, book.isbn)\n                assertEquals(firstBook.level, book.level)\n                assertEquals(firstBook.id, book.id)\n            }\n            if (i == books.size - 1) {\n                assertEquals(lastBook!!.isSaved, book.isSaved)\n                assertEquals(lastBook.bookName, book.bookName)\n                assertNull(lastBook.pages)\n                assertEquals(lastBook.isPublished, book.isPublished)\n                assertEquals(lastBook.price, book.price)\n                assertEquals(lastBook.area, book.area)\n                assertEquals(lastBook.isbn, book.isbn)\n                assertEquals(lastBook.level, book.level)\n                assertEquals(lastBook.id, book.id)\n            }\n        }\n    }\n\n    @Test\n    fun testWhere() {\n        val books = LitePal.where(\"bookname = ?\", \"Android First Line\").find<Book>()\n        val firstBook = LitePal.where(\"bookname = ?\", \"Android First Line\").findFirst<Book>()\n        val lastBook = LitePal.where(\"bookname = ?\", \"Android First Line\").findLast<Book>()\n        for (i in books.indices) {\n            val book = books[i]\n            assertTrue(book.isSaved)\n            assertEquals(\"Android First Line\", book.bookName)\n            assertTrue(450 == book.pages)\n            assertEquals(49.99, book.price)\n            assertEquals(false, book.isPublished)\n            assertEquals('A', book.level)\n            assertEquals(10.5f, book.area)\n            if (i == 0) {\n                assertEquals(firstBook!!.isSaved, book.isSaved)\n                assertEquals(firstBook.bookName, book.bookName)\n                assertEquals(firstBook.pages, book.pages)\n                assertEquals(firstBook.isPublished, book.isPublished)\n                assertEquals(firstBook.price, book.price)\n                assertEquals(firstBook.area, book.area)\n                assertEquals(firstBook.isbn, book.isbn)\n                assertEquals(firstBook.level, book.level)\n                assertEquals(firstBook.id, book.id)\n            }\n            if (i == books.size - 1) {\n                assertEquals(lastBook!!.isSaved, book.isSaved)\n                assertEquals(lastBook.bookName, book.bookName)\n                assertEquals(lastBook.pages, book.pages)\n                assertEquals(lastBook.isPublished, book.isPublished)\n                assertEquals(lastBook.price, book.price)\n                assertEquals(lastBook.area, book.area)\n                assertEquals(lastBook.isbn, book.isbn)\n                assertEquals(lastBook.level, book.level)\n                assertEquals(lastBook.id, book.id)\n            }\n        }\n        val expectedBooks = getBooks(null, \"bookname like ?\",\n                arrayOf(\"Android%Line\"), null, null, null, null)\n        val realBooks = LitePal.where(\"bookname like ?\", \"Android%Line\").find<Book>()\n        assertEquals(expectedBooks.size, realBooks.size)\n    }\n\n    @Test\n    fun testOrder() {\n        val books = LitePal.order(\"ID\").find<Book>()\n        val firstBook = LitePal.order(\"ID\").findFirst<Book>()\n        val lastBook = LitePal.order(\"ID\").findLast<Book>()\n        var preBook: Book? = null\n        for (i in books.indices) {\n            val book = books[i]\n            assertTrue(book.isSaved)\n            if (preBook != null) {\n                assertTrue(book.id > preBook.id)\n            }\n            preBook = book\n            if (i == 0) {\n                assertEquals(firstBook!!.isSaved, book.isSaved)\n                assertEquals(firstBook.bookName, book.bookName)\n                assertEquals(firstBook.pages, book.pages)\n                assertEquals(firstBook.isPublished, book.isPublished)\n                assertEquals(firstBook.price, book.price)\n                assertEquals(firstBook.area, book.area)\n                assertEquals(firstBook.isbn, book.isbn)\n                assertEquals(firstBook.level, book.level)\n                assertEquals(firstBook.id, book.id)\n            }\n            if (i == books.size - 1) {\n                assertEquals(lastBook!!.isSaved, book.isSaved)\n                assertEquals(lastBook.bookName, book.bookName)\n                assertEquals(lastBook.pages, book.pages)\n                assertEquals(lastBook.isPublished, book.isPublished)\n                assertEquals(lastBook.price, book.price)\n                assertEquals(lastBook.area, book.area)\n                assertEquals(lastBook.isbn, book.isbn)\n                assertEquals(lastBook.level, book.level)\n                assertEquals(lastBook.id, book.id)\n            }\n        }\n        val inverseBooks = LitePal.order(\"ID desc\").find<Book>()\n        val inverseFirstBook = LitePal.order(\"ID desc\").findFirst<Book>()\n        val inverseLastBook = LitePal.order(\"ID desc\").findLast<Book>()\n        var inversePreBook: Book? = null\n        for (i in inverseBooks.indices) {\n            val book = inverseBooks[i]\n            assertTrue(book.isSaved)\n            if (inversePreBook != null) {\n                assertTrue(book.id < inversePreBook.id)\n            }\n            inversePreBook = book\n            if (i == 0) {\n                assertEquals(inverseFirstBook!!.isSaved, book.isSaved)\n                assertEquals(inverseFirstBook.bookName, book.bookName)\n                assertEquals(inverseFirstBook.pages, book.pages)\n                assertEquals(inverseFirstBook.isPublished, book.isPublished)\n                assertEquals(inverseFirstBook.price, book.price)\n                assertEquals(inverseFirstBook.area, book.area)\n                assertEquals(inverseFirstBook.isbn, book.isbn)\n                assertEquals(inverseFirstBook.level, book.level)\n                assertEquals(inverseFirstBook.id, book.id)\n            }\n            if (i == books.size - 1) {\n                assertEquals(inverseLastBook!!.isSaved, book.isSaved)\n                assertEquals(inverseLastBook.bookName, book.bookName)\n                assertEquals(inverseLastBook.pages, book.pages)\n                assertEquals(inverseLastBook.isPublished, book.isPublished)\n                assertEquals(inverseLastBook.price, book.price)\n                assertEquals(inverseLastBook.area, book.area)\n                assertEquals(inverseLastBook.isbn, book.isbn)\n                assertEquals(inverseLastBook.level, book.level)\n                assertEquals(inverseLastBook.id, book.id)\n            }\n        }\n    }\n\n    @Test\n    fun testLimit() {\n        var bookList = LitePal.limit(1).find<Book>()\n        assertEquals(1, bookList.size)\n        var book = bookList[0]\n        assertTrue(book.isSaved)\n        val firstBook = LitePal.findFirst<Book>()\n        assertTrue(firstBook!!.isSaved)\n        assertEquals(firstBook.bookName, book.bookName)\n        assertEquals(firstBook.pages, book.pages)\n        assertEquals(firstBook.isPublished, book.isPublished)\n        assertEquals(firstBook.area, book.area)\n        assertEquals(firstBook.price, book.price)\n        assertEquals(firstBook.isbn, book.isbn)\n        assertEquals(firstBook.level, book.level)\n        assertEquals(firstBook.id, book.id)\n        bookList = LitePal.order(\"id desc\").limit(1).find()\n        assertEquals(1, bookList.size)\n        book = bookList[0]\n        assertTrue(book.isSaved)\n        val lastBook = LitePal.findLast(Book::class.java)\n        assertTrue(lastBook!!.isSaved)\n        assertEquals(lastBook.bookName, book.bookName)\n        assertEquals(lastBook.pages, book.pages)\n        assertEquals(lastBook.isPublished, book.isPublished)\n        assertEquals(lastBook.area, book.area)\n        assertEquals(lastBook.price, book.price)\n        assertEquals(lastBook.isbn, book.isbn)\n        assertEquals(lastBook.level, book.level)\n        assertEquals(lastBook.id, book.id)\n    }\n\n    @Test\n    fun testOffset() {\n        val list = LitePal.offset(1).find<Book>()\n        assertEquals(0, list.size)\n        val bookList = LitePal.limit(1).offset(1).find<Book>()\n        assertEquals(1, bookList.size)\n        val book = bookList[0]\n        assertTrue(book.isSaved)\n        val expectedBooks = getBooks(null, null, null, null, null, null, null)\n        val expectedBook = expectedBooks[1]\n        assertEquals(expectedBook.bookName, book.bookName)\n        assertEquals(expectedBook.pages, book.pages)\n        assertEquals(expectedBook.isPublished, book.isPublished)\n        assertEquals(expectedBook.area, book.area)\n        assertEquals(expectedBook.price, book.price)\n        assertEquals(expectedBook.isbn, book.isbn)\n        assertEquals(expectedBook.level, book.level)\n        assertEquals(expectedBook.id, book.id)\n    }\n\n    @Test\n    fun testCluster() {\n        val ids = LongArray(3)\n        for (i in 0..2) {\n            val book = Book()\n            book.pages = 5555\n            book.isPublished = true\n            book.price = 40.99\n            book.save()\n            ids[i] = book.id\n        }\n        val books = LitePal\n                .select(\"pages\", \"isPublished\")\n                .where(\"id=? or id=? or id=?\", ids[0].toString(), ids[1].toString(),\n                        ids[2].toString()).order(\"id\").limit(2).offset(1).find<Book>()\n        val firstBook = LitePal\n                .select(\"pages\", \"isPublished\")\n                .where(\"id=? or id=? or id=?\", ids[0].toString(), ids[1].toString(),\n                        ids[2].toString()).order(\"id\").limit(2).offset(1).findFirst<Book>()\n        val lastBook = LitePal\n                .select(\"pages\", \"isPublished\")\n                .where(\"id=? or id=? or id=?\", ids[0].toString(), ids[1].toString(),\n                        ids[2].toString()).order(\"id\").limit(2).offset(1).findLast<Book>()\n        assertEquals(2, books.size)\n        assertTrue(books[0].id < books[1].id)\n        for (i in 0..1) {\n            val b = books[i]\n            assertEquals(ids[i + 1], b.id)\n            assertTrue(b.isSaved)\n            assertNull(b.bookName)\n            assertTrue(5555 == b.pages)\n            assertEquals(true, b.isPublished)\n            assertEquals(0f, b.area)\n            assertEquals(0.0, b.price)\n            assertEquals(0, b.isbn.toInt())\n            assertEquals(0, b.level.toInt())\n            if (i == 0) {\n                assertEquals(firstBook!!.isSaved, b.isSaved)\n                assertEquals(firstBook.bookName, b.bookName)\n                assertEquals(firstBook.pages, b.pages)\n                assertEquals(firstBook.isPublished, b.isPublished)\n                assertEquals(firstBook.price, b.price)\n                assertEquals(firstBook.area, b.area)\n                assertEquals(firstBook.isbn, b.isbn)\n                assertEquals(firstBook.level, b.level)\n                assertEquals(firstBook.id, b.id)\n            }\n            if (i == books.size - 1) {\n                assertEquals(lastBook!!.isSaved, b.isSaved)\n                assertEquals(lastBook.bookName, b.bookName)\n                assertEquals(lastBook.pages, b.pages)\n                assertEquals(lastBook.isPublished, b.isPublished)\n                assertEquals(lastBook.price, b.price)\n                assertEquals(lastBook.area, b.area)\n                assertEquals(lastBook.isbn, b.isbn)\n                assertEquals(lastBook.level, b.level)\n                assertEquals(lastBook.id, b.id)\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryClusterTest.java",
    "content": "package com.litepaltest.test.crud.query;\n\nimport androidx.test.filters.SmallTest;\n\nimport java.util.List;\n\nimport org.junit.Test;\nimport org.litepal.LitePal;\n\nimport com.litepaltest.model.Book;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertNotNull;\nimport static junit.framework.TestCase.assertNull;\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class QueryClusterTest extends LitePalTestCase {\n\n    @Test\n\tpublic void testSelect() {\n\t\tList<Book> expectedBooks = getBooks(null, null, null, null, null, null, null);\n\t\tList<Book> books = LitePal.select(\"bookname\", \"price\").find(Book.class);\n\t\tassertEquals(expectedBooks.size(), books.size());\n        Book firstBook = LitePal.select(\"bookname\", \"price\").findFirst(Book.class);\n        Book lastBook = LitePal.select(\"bookname\", \"price\").findLast(Book.class);\n        assertNotNull(firstBook);\n\t\tfor (int i = 0; i < books.size(); i++) {\n\t\t\tBook book = books.get(i);\n\t\t\tassertTrue(book.isSaved());\n\t\t\tassertEquals(expectedBooks.get(i).getBookName(), book.getBookName());\n\t\t\tassertNull(book.getPages());\n\t\t\tassertFalse(book.isPublished());\n\t\t\tassertEquals(0f, book.getArea());\n\t\t\tassertEquals(expectedBooks.get(i).getPrice(), book.getPrice());\n\t\t\tassertEquals(0, book.getIsbn());\n\t\t\tassertEquals(0, book.getLevel());\n\t\t\tassertEquals(expectedBooks.get(i).getId(), book.getId());\n            if (i == 0) {\n                assertEquals(firstBook.isSaved(), book.isSaved());\n                assertEquals(firstBook.getBookName(), book.getBookName());\n                assertEquals(firstBook.getPages(), book.getPages());\n                assertEquals(firstBook.isPublished(), book.isPublished());\n                assertEquals(firstBook.getPrice(), book.getPrice());\n                assertEquals(firstBook.getArea(), book.getArea());\n                assertEquals(firstBook.getIsbn(), book.getIsbn());\n                assertEquals(firstBook.getLevel(), book.getLevel());\n                assertEquals(firstBook.getId(), book.getId());\n            }\n            if (i == books.size() - 1) {\n                assertEquals(lastBook.isSaved(), book.isSaved());\n                assertEquals(lastBook.getBookName(), book.getBookName());\n                assertEquals(lastBook.getPages(), book.getPages());\n                assertEquals(lastBook.isPublished(), book.isPublished());\n                assertEquals(lastBook.getPrice(), book.getPrice());\n                assertEquals(lastBook.getArea(), book.getArea());\n                assertEquals(lastBook.getIsbn(), book.getIsbn());\n                assertEquals(lastBook.getLevel(), book.getLevel());\n                assertEquals(lastBook.getId(), book.getId());\n            }\n\t\t}\n\t}\n\n    @Test\n\tpublic void testWhere() {\n\t\tList<Book> books = LitePal.where(\"bookname = ?\", \"Android First Line\").find(Book.class);\n        Book firstBook = LitePal.where(\"bookname = ?\", \"Android First Line\").findFirst(Book.class);\n        Book lastBook = LitePal.where(\"bookname = ?\", \"Android First Line\").findLast(Book.class);\n\t\tfor (int i = 0; i < books.size(); i++) {\n            Book book = books.get(i);\n\t\t\tassertTrue(book.isSaved());\n\t\t\tassertEquals(\"Android First Line\", book.getBookName());\n\t\t\tassertEquals(450, (int) book.getPages());\n\t\t\tassertEquals(49.99, book.getPrice());\n\t\t\tassertFalse(book.isPublished());\n\t\t\tassertEquals('A', book.getLevel());\n\t\t\tassertEquals(10.5f, book.getArea());\n            if (i == 0) {\n                assertEquals(firstBook.isSaved(), book.isSaved());\n                assertEquals(firstBook.getBookName(), book.getBookName());\n                assertEquals(firstBook.getPages(), book.getPages());\n                assertEquals(firstBook.isPublished(), book.isPublished());\n                assertEquals(firstBook.getPrice(), book.getPrice());\n                assertEquals(firstBook.getArea(), book.getArea());\n                assertEquals(firstBook.getIsbn(), book.getIsbn());\n                assertEquals(firstBook.getLevel(), book.getLevel());\n                assertEquals(firstBook.getId(), book.getId());\n            }\n            if (i == books.size() - 1) {\n                assertEquals(lastBook.isSaved(), book.isSaved());\n                assertEquals(lastBook.getBookName(), book.getBookName());\n                assertEquals(lastBook.getPages(), book.getPages());\n                assertEquals(lastBook.isPublished(), book.isPublished());\n                assertEquals(lastBook.getPrice(), book.getPrice());\n                assertEquals(lastBook.getArea(), book.getArea());\n                assertEquals(lastBook.getIsbn(), book.getIsbn());\n                assertEquals(lastBook.getLevel(), book.getLevel());\n                assertEquals(lastBook.getId(), book.getId());\n            }\n\t\t}\n\t\tList<Book> expectedBooks = getBooks(null, \"bookname like ?\",\n\t\t\t\tnew String[] { \"Android%Line\" }, null, null, null, null);\n\t\tList<Book> realBooks = LitePal.where(\"bookname like ?\", \"Android%Line\")\n\t\t\t\t.find(Book.class);\n\t\tassertEquals(expectedBooks.size(), realBooks.size());\n\n\n\t}\n\n    @Test\n\tpublic void testOrder() {\n\t\tList<Book> books = LitePal.order(\"ID\").find(Book.class);\n\t\tBook firstBook = LitePal.order(\"ID\").findFirst(Book.class);\n\t\tBook lastBook = LitePal.order(\"ID\").findLast(Book.class);\n        Book preBook = null;\n\t\tfor (int i = 0; i < books.size(); i++) {\n\t\t\tBook book = books.get(i);\n\t\t\tassertTrue(book.isSaved());\n\t\t\tif (preBook != null) {\n\t\t\t\tassertTrue(book.getId() > preBook.getId());\n\t\t\t}\n            preBook = book;\n            if (i == 0) {\n                assertEquals(firstBook.isSaved(), book.isSaved());\n                assertEquals(firstBook.getBookName(), book.getBookName());\n                assertEquals(firstBook.getPages(), book.getPages());\n                assertEquals(firstBook.isPublished(), book.isPublished());\n                assertEquals(firstBook.getPrice(), book.getPrice());\n                assertEquals(firstBook.getArea(), book.getArea());\n                assertEquals(firstBook.getIsbn(), book.getIsbn());\n                assertEquals(firstBook.getLevel(), book.getLevel());\n                assertEquals(firstBook.getId(), book.getId());\n            }\n            if (i == books.size() - 1) {\n                assertEquals(lastBook.isSaved(), book.isSaved());\n                assertEquals(lastBook.getBookName(), book.getBookName());\n                assertEquals(lastBook.getPages(), book.getPages());\n                assertEquals(lastBook.isPublished(), book.isPublished());\n                assertEquals(lastBook.getPrice(), book.getPrice());\n                assertEquals(lastBook.getArea(), book.getArea());\n                assertEquals(lastBook.getIsbn(), book.getIsbn());\n                assertEquals(lastBook.getLevel(), book.getLevel());\n                assertEquals(lastBook.getId(), book.getId());\n            }\n\t\t}\n\t\tList<Book> inverseBooks = LitePal.order(\"ID desc\").find(Book.class);\n        Book inverseFirstBook = LitePal.order(\"ID desc\").findFirst(Book.class);\n        Book inverseLastBook = LitePal.order(\"ID desc\").findLast(Book.class);\n\t\tBook inversePreBook = null;\n\t\tfor (int i = 0; i < inverseBooks.size(); i++) {\n\t\t\tBook book = inverseBooks.get(i);\n\t\t\tassertTrue(book.isSaved());\n\t\t\tif (inversePreBook != null) {\n\t\t\t\tassertTrue(book.getId() < inversePreBook.getId());\n\t\t\t}\n            inversePreBook = book;\n            if (i == 0) {\n                assertEquals(inverseFirstBook.isSaved(), book.isSaved());\n                assertEquals(inverseFirstBook.getBookName(), book.getBookName());\n                assertEquals(inverseFirstBook.getPages(), book.getPages());\n                assertEquals(inverseFirstBook.isPublished(), book.isPublished());\n                assertEquals(inverseFirstBook.getPrice(), book.getPrice());\n                assertEquals(inverseFirstBook.getArea(), book.getArea());\n                assertEquals(inverseFirstBook.getIsbn(), book.getIsbn());\n                assertEquals(inverseFirstBook.getLevel(), book.getLevel());\n                assertEquals(inverseFirstBook.getId(), book.getId());\n            }\n            if (i == books.size() - 1) {\n                assertEquals(inverseLastBook.isSaved(), book.isSaved());\n                assertEquals(inverseLastBook.getBookName(), book.getBookName());\n                assertEquals(inverseLastBook.getPages(), book.getPages());\n                assertEquals(inverseLastBook.isPublished(), book.isPublished());\n                assertEquals(inverseLastBook.getPrice(), book.getPrice());\n                assertEquals(inverseLastBook.getArea(), book.getArea());\n                assertEquals(inverseLastBook.getIsbn(), book.getIsbn());\n                assertEquals(inverseLastBook.getLevel(), book.getLevel());\n                assertEquals(inverseLastBook.getId(), book.getId());\n            }\n\t\t}\n\t}\n\n    @Test\n\tpublic void testLimit() {\n\t\tList<Book> bookList = LitePal.limit(1).find(Book.class);\n\t\tassertEquals(1, bookList.size());\n\t\tBook book = bookList.get(0);\n\t\tassertTrue(book.isSaved());\n\t\tBook firstBook = LitePal.findFirst(Book.class);\n\t\tassertTrue(firstBook.isSaved());\n\t\tassertEquals(firstBook.getBookName(), book.getBookName());\n\t\tassertEquals(firstBook.getPages(), book.getPages());\n\t\tassertEquals(firstBook.isPublished(), book.isPublished());\n\t\tassertEquals(firstBook.getArea(), book.getArea());\n\t\tassertEquals(firstBook.getPrice(), book.getPrice());\n\t\tassertEquals(firstBook.getIsbn(), book.getIsbn());\n\t\tassertEquals(firstBook.getLevel(), book.getLevel());\n\t\tassertEquals(firstBook.getId(), book.getId());\n\t\tbookList = LitePal.order(\"id desc\").limit(1).find(Book.class);\n\t\tassertEquals(1, bookList.size());\n\t\tbook = bookList.get(0);\n\t\tassertTrue(book.isSaved());\n\t\tBook lastBook = LitePal.findLast(Book.class);\n\t\tassertTrue(lastBook.isSaved());\n\t\tassertEquals(lastBook.getBookName(), book.getBookName());\n\t\tassertEquals(lastBook.getPages(), book.getPages());\n\t\tassertEquals(lastBook.isPublished(), book.isPublished());\n\t\tassertEquals(lastBook.getArea(), book.getArea());\n\t\tassertEquals(lastBook.getPrice(), book.getPrice());\n\t\tassertEquals(lastBook.getIsbn(), book.getIsbn());\n\t\tassertEquals(lastBook.getLevel(), book.getLevel());\n\t\tassertEquals(lastBook.getId(), book.getId());\n\t}\n\n    @Test\n\tpublic void testOffset() {\n\t\tList<Book> list = LitePal.offset(1).find(Book.class);\n\t\tassertEquals(0, list.size());\n\t\tList<Book> bookList = LitePal.limit(1).offset(1).find(Book.class);\n\t\tassertEquals(1, bookList.size());\n\t\tBook book = bookList.get(0);\n\t\tassertTrue(book.isSaved());\n\t\tList<Book> expectedBooks = getBooks(null, null, null, null, null, null, null);\n\t\tBook expectedBook = expectedBooks.get(1);\n\t\tassertEquals(expectedBook.getBookName(), book.getBookName());\n\t\tassertEquals(expectedBook.getPages(), book.getPages());\n\t\tassertEquals(expectedBook.isPublished(), book.isPublished());\n\t\tassertEquals(expectedBook.getArea(), book.getArea());\n\t\tassertEquals(expectedBook.getPrice(), book.getPrice());\n\t\tassertEquals(expectedBook.getIsbn(), book.getIsbn());\n\t\tassertEquals(expectedBook.getLevel(), book.getLevel());\n\t\tassertEquals(expectedBook.getId(), book.getId());\n\t}\n\n    @Test\n\tpublic void testCluster() {\n\t\tlong[] ids = new long[3];\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tBook book = new Book();\n\t\t\tbook.setPages(5555);\n\t\t\tbook.setPublished(true);\n\t\t\tbook.setPrice(40.99);\n\t\t\tbook.save();\n\t\t\tids[i] = book.getId();\n\t\t}\n\t\tList<Book> books = LitePal\n\t\t\t\t.select(\"pages\", \"isPublished\")\n\t\t\t\t.where(\"id=? or id=? or id=?\", String.valueOf(ids[0]), String.valueOf(ids[1]),\n\t\t\t\t\t\tString.valueOf(ids[2])).order(\"id\").limit(2).offset(1).find(Book.class);\n        Book firstBook = LitePal\n\t\t\t\t.select(\"pages\", \"isPublished\")\n\t\t\t\t.where(\"id=? or id=? or id=?\", String.valueOf(ids[0]), String.valueOf(ids[1]),\n\t\t\t\t\t\tString.valueOf(ids[2])).order(\"id\").limit(2).offset(1).findFirst(Book.class);\n        Book lastBook = LitePal\n                .select(\"pages\", \"isPublished\")\n                .where(\"id=? or id=? or id=?\", String.valueOf(ids[0]), String.valueOf(ids[1]),\n                        String.valueOf(ids[2])).order(\"id\").limit(2).offset(1).findLast(Book.class);\n\t\tassertEquals(2, books.size());\n\t\tassertTrue(books.get(0).getId() < books.get(1).getId());\n\t\tfor (int i = 0; i < 2; i++) {\n\t\t\tBook b = books.get(i);\n\t\t\tassertEquals(ids[i + 1], b.getId());\n\t\t\tassertTrue(b.isSaved());\n\t\t\tassertNull(b.getBookName());\n\t\t\tassertEquals(5555, (int) b.getPages());\n\t\t\tassertTrue(b.isPublished());\n\t\t\tassertEquals(0f, b.getArea());\n\t\t\tassertEquals(0.0, b.getPrice());\n\t\t\tassertEquals(0, b.getIsbn());\n\t\t\tassertEquals(0, b.getLevel());\n            if (i == 0) {\n                assertEquals(firstBook.isSaved(), b.isSaved());\n                assertEquals(firstBook.getBookName(), b.getBookName());\n                assertEquals(firstBook.getPages(), b.getPages());\n                assertEquals(firstBook.isPublished(), b.isPublished());\n                assertEquals(firstBook.getPrice(), b.getPrice());\n                assertEquals(firstBook.getArea(), b.getArea());\n                assertEquals(firstBook.getIsbn(), b.getIsbn());\n                assertEquals(firstBook.getLevel(), b.getLevel());\n                assertEquals(firstBook.getId(), b.getId());\n            }\n            if (i == books.size() - 1) {\n                assertEquals(lastBook.isSaved(), b.isSaved());\n                assertEquals(lastBook.getBookName(), b.getBookName());\n                assertEquals(lastBook.getPages(), b.getPages());\n                assertEquals(lastBook.isPublished(), b.isPublished());\n                assertEquals(lastBook.getPrice(), b.getPrice());\n                assertEquals(lastBook.getArea(), b.getArea());\n                assertEquals(lastBook.getIsbn(), b.getIsbn());\n                assertEquals(lastBook.getLevel(), b.getLevel());\n                assertEquals(lastBook.getId(), b.getId());\n            }\n\t\t}\n\n\t\tBook first1 = LitePal.findFirst(Book.class);\n\t\tBook first2 = LitePal.select(\"id\").findFirst(Book.class);\n\t\tassertEquals(first1.getId(), first2.getId());\n\n\t\tBook last1 = LitePal.findLast(Book.class);\n\t\tBook last2 = LitePal.select(\"id\").findLast(Book.class);\n\t\tassertEquals(last1.getId(), last2.getId());\n\n\t\tList<Book> firstTwoBooks = LitePal.where(\"id=? or id=? or id=?\", String.valueOf(ids[0]), String.valueOf(ids[1]),\n\t\t\t\tString.valueOf(ids[2])).order(\"id\").limit(2).find(Book.class);\n\t\tfirstBook = LitePal.where(\"id=? or id=? or id=?\", String.valueOf(ids[0]), String.valueOf(ids[1]),\n\t\t\t\tString.valueOf(ids[2])).order(\"id\").limit(2).findFirst(Book.class);\n\t\tlastBook = LitePal.where(\"id=? or id=? or id=?\", String.valueOf(ids[0]), String.valueOf(ids[1]),\n\t\t\t\tString.valueOf(ids[2])).order(\"id\").limit(2).findLast(Book.class);\n\t\tassertEquals(firstTwoBooks.get(0).getId(), firstBook.getId());\n\t\tassertEquals(firstTwoBooks.get(1).getId(), lastBook.getId());\n\t}\n\n\t@Test\n\tpublic void testBooleanQuery() {\n    \tBook book1 = new Book();\n    \tbook1.setBookName(\"not published\");\n    \tbook1.setPublished(false);\n    \tBook book2 = new Book();\n    \tbook2.setBookName(\"published\");\n    \tbook2.setPublished(true);\n    \tbook1.save();\n    \tbook2.save();\n\n    \tBook book1DB = LitePal.where(\"isPublished = 0\").findFirst(Book.class);\n    \tBook book2DB = LitePal.where(\"isPublished = 1\").findFirst(Book.class);\n    \tassertNotNull(book1DB);\n    \tassertNotNull(book2DB);\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryDateTest.java",
    "content": "package com.litepaltest.test.crud.query;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Student;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Test;\nimport org.litepal.LitePal;\n\nimport java.util.Calendar;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class QueryDateTest extends LitePalTestCase {\n\n\t@Test\n\tpublic void testQueryDate() {\n\t\tCalendar calendar = Calendar.getInstance();\n\t\tcalendar.clear();\n\t\tcalendar.set(1990, 9, 16, 0, 0, 0);\n\t\tStudent student1 = new Student();\n\t\tstudent1.setName(\"Student 1\");\n\t\tstudent1.setBirthday(calendar.getTime());\n\t\tstudent1.save();\n\t\tStudent studentFromDB = LitePal.find(Student.class, student1.getId());\n\t\tassertEquals(\"Student 1\", studentFromDB.getName());\n\t\tassertEquals(calendar.getTimeInMillis(), studentFromDB.getBirthday().getTime());\n\t}\n\n@Test\n\tpublic void testQueryDateBefore1970() {\n\t\tCalendar calendar = Calendar.getInstance();\n\t\tcalendar.clear();\n\t\tcalendar.set(1920, 6, 3, 0, 0, 0);\n\t\tStudent student1 = new Student();\n\t\tstudent1.setName(\"Student 2\");\n\t\tstudent1.setBirthday(calendar.getTime());\n\t\tstudent1.save();\n\t\tStudent studentFromDB = LitePal.find(Student.class, student1.getId());\n\t\tassertEquals(\"Student 2\", studentFromDB.getName());\n\t\tassertEquals(calendar.getTimeInMillis(), studentFromDB.getBirthday().getTime());\n\t}\n\n\t@Test\n\tpublic void testQueryDateWithDefaultValue() {\n\t\tStudent student = new Student();\n\t\tstudent.setName(\"School Student\");\n\t\tassertTrue(student.save());\n\t\tStudent studentFromDB = LitePal.find(Student.class, student.getId());\n\t\tassertEquals(1589203961859L, studentFromDB.getSchoolDate().getTime());\n\t}\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryEagerKotlinTest.kt",
    "content": "package com.litepaltest.test.crud.query\n\nimport androidx.test.filters.SmallTest\nimport com.litepaltest.model.Classroom\nimport com.litepaltest.model.IdCard\nimport com.litepaltest.model.Student\nimport com.litepaltest.model.Teacher\nimport junit.framework.TestCase.*\nimport org.junit.Before\nimport org.junit.Test\nimport org.litepal.LitePal\nimport org.litepal.extension.deleteAll\nimport org.litepal.extension.find\nimport org.litepal.extension.findAll\nimport org.litepal.extension.findLast\nimport java.util.*\n\n@SmallTest\nclass QueryEagerKotlinTest {\n\n    private var classroom: Classroom? = null\n\n    private var student1: Student? = null\n\n    private var student2: Student? = null\n\n    private var student3: Student? = null\n\n    private var teacher1: Teacher? = null\n\n    private var teacher2: Teacher? = null\n\n    private var idcard1: IdCard? = null\n\n    private var idcard2: IdCard? = null\n\n    @Before\n    fun setUp() {\n        val calendar = Calendar.getInstance()\n        classroom = Classroom()\n        classroom!!.name = \"Classroom 11\"\n        idcard1 = IdCard()\n        idcard1!!.number = \"320311\"\n        idcard2 = IdCard()\n        idcard2!!.number = \"320322\"\n        calendar.clear()\n        calendar.set(1990, 9, 16, 0, 0, 0)\n        student1 = Student()\n        student1!!.name = \"Student 1\"\n        student1!!.classroom = classroom\n        student1!!.idcard = idcard1\n        student1!!.birthday = calendar.time\n        calendar.clear()\n        calendar.set(1989, 7, 7, 0, 0, 0)\n        student2 = Student()\n        student2!!.name = \"Student 2\"\n        student2!!.classroom = classroom\n        student2!!.birthday = calendar.time\n        student3 = Student()\n        student3!!.name = \"Student 3\"\n        teacher1 = Teacher()\n        teacher1!!.teacherName = \"Teacher 1\"\n        teacher1!!.teachYears = 3\n        teacher1!!.idCard = idcard2\n        teacher2 = Teacher()\n        teacher2!!.isSex = false\n        teacher2!!.teacherName = \"Teacher 2\"\n        student1!!.teachers.add(teacher1)\n        student1!!.teachers.add(teacher2)\n        student2!!.teachers.add(teacher2)\n        classroom!!.teachers.add(teacher1)\n        classroom!!.save()\n        student1!!.save()\n        student2!!.save()\n        student3!!.save()\n        idcard1!!.save()\n        idcard2!!.save()\n        teacher1!!.save()\n        teacher2!!.save()\n    }\n\n    @Test\n    fun testEagerFind() {\n        var s1 = LitePal.find<Student>(student1!!.id.toLong(), true)\n        var c: Classroom? = s1!!.classroom\n        val ic = s1.idcard\n        val tList = s1.teachers\n        assertNotNull(c)\n        assertNotNull(ic)\n        assertEquals(classroom!!._id, c!!._id)\n        assertEquals(\"Classroom 11\", c.name)\n        assertEquals(idcard1!!.id, ic.id)\n        assertEquals(\"320311\", ic.number)\n        assertEquals(student1!!.teachers.size, tList.size)\n        val calendar = Calendar.getInstance()\n        calendar.clear()\n        calendar.set(1990, 9, 16, 0, 0, 0)\n        assertEquals(calendar.time.time, s1.birthday.time)\n        for (t in tList) {\n            if (t.id == teacher1!!.id) {\n                assertEquals(\"Teacher 1\", t.teacherName)\n                assertEquals(teacher1!!.teachYears, t.teachYears)\n                assertTrue(t.isSex)\n                continue\n            }\n            if (t.id == teacher2!!.id) {\n                assertEquals(\"Teacher 2\", t.teacherName)\n                assertFalse(t.isSex)\n                continue\n            }\n            fail()\n        }\n        s1 = LitePal.find<Student>(student1!!.id.toLong())\n        c = s1!!.classroom\n        assertNull(c)\n        assertNull(s1.idcard)\n        assertEquals(0, s1.teachers.size)\n        c = LitePal.find<Classroom>(classroom!!._id.toLong(), true)\n        assertEquals(2, c!!.studentCollection.size)\n        assertEquals(1, c.teachers.size)\n        for (s in c.studentCollection) {\n            if (s.id == student1!!.id) {\n                assertEquals(\"Student 1\", s.name)\n                continue\n            }\n            if (s.id == student2!!.id) {\n                assertEquals(\"Student 2\", s.name)\n                calendar.clear()\n                calendar.set(1989, 7, 7, 0, 0, 0)\n                assertEquals(calendar.time.time, s.birthday.time)\n                continue\n            }\n            fail()\n        }\n        val t1 = LitePal.find<Teacher>(teacher2!!.id.toLong(), true)\n        val sList = t1!!.students\n        assertEquals(teacher2!!.students.size, sList.size)\n        for (s in sList) {\n            if (s.id == student1!!.id) {\n                assertEquals(\"Student 1\", s.name)\n                calendar.clear()\n                calendar.set(1990, 9, 16, 0, 0, 0)\n                assertEquals(calendar.time.time, s.birthday.time)\n                continue\n            }\n            if (s.id == student2!!.id) {\n                assertEquals(\"Student 2\", s.name)\n                continue\n            }\n            fail()\n        }\n        val s3 = LitePal.find<Student>(student3!!.id.toLong())\n        assertNull(s3!!.birthday)\n    }\n\n    private fun resetData() {\n        LitePal.deleteAll<Student>()\n        LitePal.deleteAll<Classroom>()\n        LitePal.deleteAll<Teacher>()\n        LitePal.deleteAll<IdCard>()\n        setUp()\n    }\n\n    @Test\n    fun testEagerFindFirst() {\n        resetData()\n        var s1 = LitePal.findFirst(Student::class.java)\n        assertNull(s1!!.classroom)\n        s1 = LitePal.findFirst(Student::class.java, true)\n        assertNotNull(s1)\n    }\n\n    @Test\n    fun testEagerFindLast() {\n        resetData()\n        var t1 = LitePal.findLast<Teacher>()\n        assertEquals(0, t1!!.students.size)\n        t1 = LitePal.findLast<Teacher>(true)\n        assertTrue(0 < t1!!.students.size)\n    }\n\n    @Test\n    fun testEagerFindAll() {\n        resetData()\n        var sList = LitePal.findAll<Student>()\n        for (s in sList) {\n            assertNull(s.classroom)\n            assertEquals(0, s.teachers.size)\n        }\n        sList = LitePal.findAll(true)\n        for (s in sList) {\n            if (s.classroom == null) {\n                continue\n            }\n            assertEquals(\"Classroom 11\", s.classroom.name)\n            assertTrue(s.teachers.size > 0)\n            val tList = s.teachers\n            for (t in tList) {\n                if (t.id == teacher1!!.id) {\n                    assertEquals(\"Teacher 1\", t.teacherName)\n                    assertEquals(teacher1!!.teachYears, t.teachYears)\n                    assertTrue(t.isSex)\n                    continue\n                }\n                if (t.id == teacher2!!.id) {\n                    assertEquals(\"Teacher 2\", t.teacherName)\n                    assertFalse(t.isSex)\n                    continue\n                }\n                fail()\n            }\n        }\n    }\n\n    @Test\n    fun testEagerClusterQuery() {\n        resetData()\n        var sList = LitePal.where(\"id = ?\", student1!!.id.toString()).find<Student>()\n        assertEquals(1, sList.size)\n        var s = sList[0]\n        assertNull(s.classroom)\n        sList = LitePal.where(\"id = ?\", student1!!.id.toString()).find(true)\n        assertEquals(1, sList.size)\n        s = sList[0]\n        assertNotNull(s.classroom)\n        val c = s.classroom\n        assertEquals(\"Classroom 11\", c.name)\n    }\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryEagerTest.java",
    "content": "package com.litepaltest.test.crud.query;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.IdCard;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.LitePal;\n\nimport java.util.Calendar;\nimport java.util.List;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertNotNull;\nimport static junit.framework.TestCase.assertNull;\nimport static junit.framework.TestCase.assertTrue;\nimport static junit.framework.TestCase.fail;\n\n@SmallTest\npublic class QueryEagerTest {\n\n\tprivate Classroom classroom;\n\n\tprivate Student student1;\n\n\tprivate Student student2;\n\t\n\tprivate Student student3;\n\n\tprivate Teacher teacher1;\n\n\tprivate Teacher teacher2;\n\n\tprivate IdCard idcard1;\n\n\t@Before\n\tpublic void setUp() {\n\t\tCalendar calendar = Calendar.getInstance();\n\t\tclassroom = new Classroom();\n\t\tclassroom.setName(\"Classroom 11\");\n\t\tidcard1 = new IdCard();\n\t\tidcard1.setNumber(\"320311\");\n\t\tIdCard idcard2 = new IdCard();\n\t\tidcard2.setNumber(\"320322\");\n\t\tcalendar.clear();\n\t\tcalendar.set(1990, 9, 16, 0, 0, 0);\n\t\tstudent1 = new Student();\n\t\tstudent1.setName(\"Student 1\");\n\t\tstudent1.setClassroom(classroom);\n\t\tstudent1.setIdcard(idcard1);\n\t\tstudent1.setBirthday(calendar.getTime());\n\t\tcalendar.clear();\n\t\tcalendar.set(1989, 7, 7, 0, 0, 0);\n\t\tstudent2 = new Student();\n\t\tstudent2.setName(\"Student 2\");\n\t\tstudent2.setClassroom(classroom);\n\t\tstudent2.setBirthday(calendar.getTime());\n\t\tstudent3 = new Student();\n\t\tstudent3.setName(\"Student 3\");\n\t\tteacher1 = new Teacher();\n\t\tteacher1.setTeacherName(\"Teacher 1\");\n\t\tteacher1.setTeachYears(3);\n\t\tteacher1.setIdCard(idcard2);\n\t\tteacher2 = new Teacher();\n\t\tteacher2.setSex(false);\n\t\tteacher2.setTeacherName(\"Teacher 2\");\n\t\tstudent1.getTeachers().add(teacher1);\n\t\tstudent1.getTeachers().add(teacher2);\n\t\tstudent2.getTeachers().add(teacher2);\n\t\tclassroom.getTeachers().add(teacher1);\n\t\tclassroom.save();\n\t\tstudent1.save();\n\t\tstudent2.save();\n\t\tstudent3.save();\n\t\tidcard1.save();\n\t\tidcard2.save();\n\t\tteacher1.save();\n\t\tteacher2.save();\n\t}\n\n    @Test\n\tpublic void testEagerFind() {\n\t\tStudent s1 = LitePal.find(Student.class, student1.getId(), true);\n\t\tClassroom c = s1.getClassroom();\n\t\tIdCard ic = s1.getIdcard();\n\t\tList<Teacher> tList = s1.getTeachers();\n\t\tassertNotNull(c);\n\t\tassertNotNull(ic);\n\t\tassertEquals(classroom.get_id(), c.get_id());\n\t\tassertEquals(\"Classroom 11\", c.getName());\n\t\tassertEquals(idcard1.getId(), ic.getId());\n\t\tassertEquals(\"320311\", ic.getNumber());\n\t\tassertEquals(student1.getTeachers().size(), tList.size());\n\t\tCalendar calendar = Calendar.getInstance();\n\t\tcalendar.clear();\n\t\tcalendar.set(1990, 9, 16, 0, 0, 0);\n\t\tassertEquals(calendar.getTime().getTime(), s1.getBirthday().getTime());\n\t\tfor (Teacher t : tList) {\n\t\t\tif (t.getId() == teacher1.getId()) {\n\t\t\t\tassertEquals(\"Teacher 1\", t.getTeacherName());\n\t\t\t\tassertEquals(teacher1.getTeachYears(), t.getTeachYears());\n\t\t\t\tassertTrue(t.isSex());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t.getId() == teacher2.getId()) {\n\t\t\t\tassertEquals(\"Teacher 2\", t.getTeacherName());\n\t\t\t\tassertFalse(t.isSex());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\t\ts1 = LitePal.find(Student.class, student1.getId());\n\t\tc = s1.getClassroom();\n\t\tassertNull(c);\n\t\tassertNull(s1.getIdcard());\n\t\tassertEquals(0, s1.getTeachers().size());\n\t\tc = LitePal.find(Classroom.class, classroom.get_id(), true);\n\t\tassertEquals(2, c.getStudentCollection().size());\n\t\tassertEquals(1, c.getTeachers().size());\n\t\tfor (Student s : c.getStudentCollection()) {\n\t\t\tif (s.getId() == student1.getId()) {\n\t\t\t\tassertEquals(\"Student 1\", s.getName());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (s.getId() == student2.getId()) {\n\t\t\t\tassertEquals(\"Student 2\", s.getName());\n\t\t\t\tcalendar.clear();\n\t\t\t\tcalendar.set(1989, 7, 7, 0, 0, 0);\n\t\t\t\tassertEquals(calendar.getTime().getTime(), s.getBirthday().getTime());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\t\tTeacher t1 = LitePal.find(Teacher.class, teacher2.getId(), true);\n\t\tList<Student> sList = t1.getStudents();\n\t\tassertEquals(teacher2.getStudents().size(), sList.size());\n\t\tfor (Student s : sList) {\n\t\t\tif (s.getId() == student1.getId()) {\n\t\t\t\tassertEquals(\"Student 1\", s.getName());\n\t\t\t\tcalendar.clear();\n\t\t\t\tcalendar.set(1990, 9, 16, 0, 0, 0);\n\t\t\t\tassertEquals(calendar.getTime().getTime(), s.getBirthday().getTime());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (s.getId() == student2.getId()) {\n\t\t\t\tassertEquals(\"Student 2\", s.getName());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfail();\n\t\t}\n\t\tStudent s3 = LitePal.find(Student.class, student3.getId());\n\t\tassertNull(s3.getBirthday());\n\t}\n\n\tpublic void resetData() {\n        LitePal.deleteAll(Student.class);\n        LitePal.deleteAll(Classroom.class);\n        LitePal.deleteAll(Teacher.class);\n        LitePal.deleteAll(IdCard.class);\n\t\tsetUp();\n\t}\n\n    @Test\n\tpublic void testEagerFindFirst() {\n\t\tresetData();\n\t\tStudent s1 = LitePal.findFirst(Student.class);\n\t\tassertNull(s1.getClassroom());\n\t\ts1 = LitePal.findFirst(Student.class, true);\n\t\tassertNotNull(s1);\n\t}\n\n    @Test\n\tpublic void testEagerFindLast() {\n\t\tresetData();\n\t\tTeacher t1 = LitePal.findLast(Teacher.class);\n\t\tassertEquals(0, t1.getStudents().size());\n\t\tt1 = LitePal.findLast(Teacher.class, true);\n\t\tassertTrue(0 < t1.getStudents().size());\n\t}\n\n    @Test\n\tpublic void testEagerFindAll() {\n\t\tresetData();\n\t\tList<Student> sList = LitePal.findAll(Student.class);\n\t\tfor (Student s : sList) {\n\t\t\tassertNull(s.getClassroom());\n\t\t\tassertEquals(0, s.getTeachers().size());\n\t\t}\n\t\tsList = LitePal.findAll(Student.class, true);\n\t\tfor (Student s : sList) {\n\t\t\tif (s.getClassroom() == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tassertEquals(\"Classroom 11\", s.getClassroom().getName());\n\t\t\tassertTrue(s.getTeachers().size() > 0);\n\t\t\tList<Teacher> tList = s.getTeachers();\n\t\t\tfor (Teacher t : tList) {\n\t\t\t\tif (t.getId() == teacher1.getId()) {\n\t\t\t\t\tassertEquals(\"Teacher 1\", t.getTeacherName());\n\t\t\t\t\tassertEquals(teacher1.getTeachYears(), t.getTeachYears());\n\t\t\t\t\tassertTrue(t.isSex());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (t.getId() == teacher2.getId()) {\n\t\t\t\t\tassertEquals(\"Teacher 2\", t.getTeacherName());\n\t\t\t\t\tassertFalse(t.isSex());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfail();\n\t\t\t}\n\t\t}\n\t}\n\n    @Test\n\tpublic void testEagerClusterQuery() {\n\t\tresetData();\n\t\tList<Student> sList = LitePal.where(\"id = ?\", String.valueOf(student1.getId())).find(\n\t\t\t\tStudent.class);\n\t\tassertEquals(1, sList.size());\n\t\tStudent s = sList.get(0);\n\t\tassertNull(s.getClassroom());\n\t\tsList = LitePal.where(\"id = ?\", String.valueOf(student1.getId())).find(Student.class, true);\n\t\tassertEquals(1, sList.size());\n\t\ts = sList.get(0);\n\t\tassertNotNull(s.getClassroom());\n\t\tClassroom c = s.getClassroom();\n\t\tassertEquals(\"Classroom 11\", c.getName());\n\t}\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryMathKotlinTest.kt",
    "content": "package com.litepaltest.test.crud.query\n\nimport androidx.test.filters.SmallTest\nimport com.litepaltest.model.Student\nimport junit.framework.TestCase.assertEquals\nimport junit.framework.TestCase.fail\nimport org.junit.Before\nimport org.junit.Test\nimport org.litepal.LitePal\nimport org.litepal.extension.*\nimport org.litepal.util.DBUtility\n\n@SmallTest\nclass QueryMathKotlinTest {\n\n    private var studentTable: String? = null\n\n    @Before\n    fun setUp() {\n        studentTable = DBUtility.getTableNameByClassName(Student::class.java.name)\n    }\n\n    @Test\n    fun testCount() {\n        var result = LitePal.count<Student>()\n        var realResult = -100\n        var cursor = LitePal.findBySQL(\"select count(1) from \" + studentTable!!)\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getInt(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n        result = LitePal.where(\"id > ?\", \"99\").count(studentTable)\n        cursor = LitePal.findBySQL(\"select count(1) from $studentTable where id > ?\", \"99\")\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getInt(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n        try {\n            LitePal.count(\"nosuchtable\")\n            fail()\n        } catch (e: Exception) {\n        }\n\n    }\n\n    @Test\n    fun testAverage() {\n        var result = LitePal.average<Student>(\"age\")\n        var realResult = -100.0\n        var cursor = LitePal.findBySQL(\"select avg(age) from \" + studentTable!!)\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getDouble(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n        result = LitePal.where(\"id > ?\", \"99\").average(studentTable, \"age\")\n        cursor = LitePal.findBySQL(\"select avg(age) from $studentTable where id > ?\", \"99\")\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getDouble(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n        try {\n            LitePal.average<Student>(\"nosuchcolumn\")\n            fail()\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n\n    }\n\n    @Test\n    fun testMax() {\n        var result = LitePal.max<Student, Int>(\"age\")\n        var realResult = -100\n        var cursor = LitePal.findBySQL(\"select max(age) from \" + studentTable!!)\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getInt(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n        result = LitePal.where(\"age < ?\", \"20\").max(studentTable!!, \"age\")\n        cursor = LitePal.findBySQL(\"select max(age) from $studentTable where age < ?\", \"20\")\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getInt(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n    }\n\n    @Test\n    fun testMin() {\n        var result = LitePal.min<Student, Int>(\"age\")\n        var realResult = -100\n        var cursor = LitePal.findBySQL(\"select min(age) from \" + studentTable!!)\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getInt(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n        result = LitePal.where(\"age > ?\", \"10\").min(studentTable!!, \"age\")\n        cursor = LitePal.findBySQL(\"select min(age) from $studentTable where age > ?\", \"10\")\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getInt(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n    }\n\n    @Test\n    fun testSum() {\n        var result = LitePal.sum<Student, Int>(\"age\")\n        var realResult = -100\n        var cursor = LitePal.findBySQL(\"select sum(age) from \" + studentTable!!)\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getInt(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n        result = LitePal.where(\"age > ?\", \"15\").sum(studentTable!!, \"age\")\n        cursor = LitePal.findBySQL(\"select sum(age) from $studentTable where age > ?\", \"15\")\n        if (cursor!!.moveToFirst()) {\n            realResult = cursor.getInt(0)\n        }\n        cursor.close()\n        assertEquals(realResult, result)\n    }\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/query/QueryMathTest.java",
    "content": "package com.litepaltest.test.crud.query;\n\nimport android.database.Cursor;\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Student;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.LitePal;\nimport org.litepal.util.DBUtility;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.fail;\n\n\n@SmallTest\npublic class QueryMathTest {\n\n    String studentTable;\n\n    @Before\n    public void setUp() {\n        studentTable = DBUtility.getTableNameByClassName(Student.class.getName());\n    }\n\n    @Test\n    public void testCount() {\n\t\tint result = LitePal.count(Student.class);\n\t\tint realResult = -100;\n\t\tCursor cursor = LitePal.findBySQL(\"select count(1) from \" + studentTable);\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getInt(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t\tresult = LitePal.where(\"id > ?\", \"99\").count(studentTable);\n\t\tcursor = LitePal.findBySQL(\"select count(1) from \" + studentTable + \" where id > ?\", \"99\");\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getInt(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t\ttry {\n            LitePal.count(\"nosuchtable\");\n\t\t\tfail();\n\t\t} catch (Exception ignored) {\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testAverage() {\n\t\tdouble result = LitePal.average(Student.class, \"age\");\n\t\tdouble realResult = -100;\n\t\tCursor cursor = LitePal.findBySQL(\"select avg(age) from \" + studentTable);\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getDouble(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t\tresult = LitePal.where(\"id > ?\", \"99\").average(studentTable, \"age\");\n\t\tcursor = LitePal.findBySQL(\"select avg(age) from \" + studentTable + \" where id > ?\", \"99\");\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getDouble(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t\ttry {\n            LitePal.average(Student.class, \"nosuchcolumn\");\n\t\t\tfail();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testMax() {\n\t\tint result = LitePal.max(Student.class, \"age\", Integer.TYPE);\n\t\tint realResult = -100;\n\t\tCursor cursor = LitePal.findBySQL(\"select max(age) from \" + studentTable);\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getInt(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t\tresult = LitePal.where(\"age < ?\", \"20\").max(studentTable, \"age\", Integer.TYPE);\n\t\tcursor = LitePal.findBySQL(\"select max(age) from \" + studentTable + \" where age < ?\", \"20\");\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getInt(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t}\n\n\t@Test\n\tpublic void testMin() {\n\t\tint result = LitePal.min(Student.class, \"age\", Integer.TYPE);\n\t\tint realResult = -100;\n\t\tCursor cursor = LitePal.findBySQL(\"select min(age) from \" + studentTable);\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getInt(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t\tresult = LitePal.where(\"age > ?\", \"10\").min(studentTable, \"age\", Integer.TYPE);\n\t\tcursor = LitePal.findBySQL(\"select min(age) from \" + studentTable + \" where age > ?\", \"10\");\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getInt(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t}\n\n\t@Test\n\tpublic void testSum() {\n\t\tint result = LitePal.sum(Student.class, \"age\", Integer.TYPE);\n\t\tint realResult = -100;\n\t\tCursor cursor = LitePal.findBySQL(\"select sum(age) from \" + studentTable);\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getInt(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t\tresult = LitePal.where(\"age > ?\", \"15\").sum(studentTable, \"age\", Integer.TYPE);\n\t\tcursor = LitePal.findBySQL(\"select sum(age) from \" + studentTable + \" where age > ?\", \"15\");\n\t\tif (cursor.moveToFirst()) {\n\t\t\trealResult = cursor.getInt(0);\n\t\t}\n\t\tcursor.close();\n\t\tassertEquals(realResult, result);\n\t}\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/save/Many2ManySaveTest.java",
    "content": "package com.litepaltest.test.crud.save;\n\nimport androidx.test.filters.SmallTest;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\nimport org.junit.Test;\nimport org.litepal.crud.LitePalSupport;\n\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class Many2ManySaveTest extends LitePalTestCase {\n\n\tprivate Student danny;\n\n\tprivate Student mick;\n\n\tprivate Teacher cam;\n\n\tprivate Teacher jack;\n\n\tprivate void init() {\n\t\tdanny = new Student();\n\t\tdanny.setName(\"Danny\");\n\t\tdanny.setAge(14);\n\t\tmick = new Student();\n\t\tmick.setName(\"Mick\");\n\t\tmick.setAge(13);\n\t\tcam = new Teacher();\n\t\tcam.setTeacherName(\"Cam\");\n\t\tcam.setAge(33);\n\t\tcam.setSex(true);\n\t\tcam.setTeachYears(5);\n\t\tjack = new Teacher();\n\t\tjack.setTeacherName(\"Jack\");\n\t\tjack.setAge(36);\n\t\tjack.setSex(false);\n\t\tjack.setTeachYears(11);\n\t}\n\n\tprivate void buildBidirectionalAssociation() {\n\t\tdanny.getTeachers().add(jack);\n\t\tdanny.getTeachers().add(cam);\n\t\tmick.getTeachers().add(jack);\n\t\tmick.getTeachers().add(cam);\n\t\tcam.getStudents().add(danny);\n\t\tcam.getStudents().add(mick);\n\t\tjack.getStudents().add(danny);\n\t\tjack.getStudents().add(mick);\n\t}\n\n\tprivate void buildUnidirectionalAssociation() {\n\t\tif (Math.random() >= 0.5) {\n\t\t\tdanny.getTeachers().add(jack);\n\t\t\tdanny.getTeachers().add(cam);\n\t\t\tmick.getTeachers().add(jack);\n\t\t\tmick.getTeachers().add(cam);\n\t\t} else {\n\t\t\tcam.getStudents().add(danny);\n\t\t\tcam.getStudents().add(mick);\n\t\t\tjack.getStudents().add(danny);\n\t\t\tjack.getStudents().add(mick);\n\t\t}\n\t}\n\n\tprivate List<LitePalSupport> getModelList() {\n\t\tList<LitePalSupport> list = new ArrayList<>();\n\t\tlist.add(jack);\n\t\tlist.add(danny);\n\t\tlist.add(cam);\n\t\tlist.add(mick);\n\t\treturn list;\n\t}\n\n\tprivate void saveAllByRandom() {\n\t\tList<LitePalSupport> modelList = getModelList();\n\t\twhile (!modelList.isEmpty()) {\n\t\t\tRandom rand = new Random();\n\t\t\tint index = rand.nextInt(modelList.size());\n            LitePalSupport model = modelList.remove(index);\n\t\t\tmodel.save();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testCase1() {\n\t\tinit();\n\t\tbuildBidirectionalAssociation();\n\t\tsaveAllByRandom();\n\t\tassertTrue(isDataExists(getTableName(danny), danny.getId()));\n\t\tassertTrue(isDataExists(getTableName(mick), mick.getId()));\n\t\tassertTrue(isDataExists(getTableName(cam), cam.getId()));\n\t\tassertTrue(isDataExists(getTableName(jack), jack.getId()));\n\t\tassertM2M(getTableName(danny), getTableName(cam), danny.getId(), cam.getId());\n\t\tassertM2M(getTableName(danny), getTableName(jack), danny.getId(), jack.getId());\n\t\tassertM2M(getTableName(mick), getTableName(cam), mick.getId(), cam.getId());\n\t\tassertM2M(getTableName(mick), getTableName(jack), mick.getId(), jack.getId());\n\t}\n\n    @Test\n\tpublic void testCase2() {\n\t\tinit();\n\t\tbuildBidirectionalAssociation();\n\t\tdanny.save();\n\t\tjack.save();\n\t\tcam.save();\n\t\tassertTrue(isDataExists(getTableName(danny), danny.getId()));\n\t\tassertFalse(isDataExists(getTableName(mick), mick.getId()));\n\t\tassertTrue(isDataExists(getTableName(cam), cam.getId()));\n\t\tassertTrue(isDataExists(getTableName(jack), jack.getId()));\n\t\tassertM2M(getTableName(danny), getTableName(cam), danny.getId(), cam.getId());\n\t\tassertM2M(getTableName(danny), getTableName(jack), danny.getId(), jack.getId());\n\t\tassertM2MFalse(getTableName(mick), getTableName(cam), mick.getId(), cam.getId());\n\t\tassertM2MFalse(getTableName(mick), getTableName(jack), mick.getId(), jack.getId());\n\t}\n\n    @Test\n\tpublic void testCase3() {\n\t\tinit();\n\t\tbuildBidirectionalAssociation();\n\t\tjack.save();\n\t\tcam.save();\n\t\tassertFalse(isDataExists(getTableName(danny), danny.getId()));\n\t\tassertFalse(isDataExists(getTableName(mick), mick.getId()));\n\t\tassertTrue(isDataExists(getTableName(cam), cam.getId()));\n\t\tassertTrue(isDataExists(getTableName(jack), jack.getId()));\n\t\tassertM2MFalse(getTableName(danny), getTableName(cam), danny.getId(), cam.getId());\n\t\tassertM2MFalse(getTableName(danny), getTableName(jack), danny.getId(), jack.getId());\n\t\tassertM2MFalse(getTableName(mick), getTableName(cam), mick.getId(), cam.getId());\n\t\tassertM2MFalse(getTableName(mick), getTableName(jack), mick.getId(), jack.getId());\n\t}\n\n    @Test\n\tpublic void testCase4() {\n\t\tinit();\n\t\tbuildUnidirectionalAssociation();\n\t\tsaveAllByRandom();\n\t\tassertTrue(isDataExists(getTableName(danny), danny.getId()));\n\t\tassertTrue(isDataExists(getTableName(mick), mick.getId()));\n\t\tassertTrue(isDataExists(getTableName(cam), cam.getId()));\n\t\tassertTrue(isDataExists(getTableName(jack), jack.getId()));\n\t\tassertM2M(getTableName(danny), getTableName(cam), danny.getId(), cam.getId());\n\t\tassertM2M(getTableName(danny), getTableName(jack), danny.getId(), jack.getId());\n\t\tassertM2M(getTableName(mick), getTableName(cam), mick.getId(), cam.getId());\n\t\tassertM2M(getTableName(mick), getTableName(jack), mick.getId(), jack.getId());\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/save/Many2OneBiSaveTest.java",
    "content": "package com.litepaltest.test.crud.save;\n\nimport androidx.test.filters.SmallTest;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class Many2OneBiSaveTest extends LitePalTestCase {\n\n\tprivate Classroom c1;\n\n\tprivate Student s1;\n\n\tprivate Student s2;\n\n\tpublic void init() {\n\t\tc1 = new Classroom();\n\t\tc1.setName(\"Computer room\");\n\t\ts1 = new Student();\n\t\ts1.setName(\"Tom\");\n\t\ts2 = new Student();\n\t\ts2.setName(\"Lily\");\n\t}\n\n    @Test\n\tpublic void testCase1() {\n\t\tinit();\n\t\tSet<Student> ss = new HashSet<>();\n\t\tss.add(s1);\n\t\tss.add(s2);\n\t\tc1.setStudentCollection(ss);\n\t\tc1.save();\n\t\ts1.save();\n\t\ts2.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase2() {\n\t\tinit();\n\t\tSet<Student> ss = new HashSet<>();\n\t\tss.add(s1);\n\t\tss.add(s2);\n\t\tc1.setStudentCollection(ss);\n\t\ts1.save();\n\t\ts2.save();\n\t\tc1.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase3() {\n\t\tinit();\n\t\tSet<Student> ss = new HashSet<>();\n\t\tss.add(s1);\n\t\tss.add(s2);\n\t\tc1.setStudentCollection(ss);\n\t\ts2.save();\n\t\tc1.save();\n\t\ts1.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase4() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tc1.save();\n\t\ts1.save();\n\t\ts2.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase5() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\ts1.save();\n\t\ts2.save();\n\t\tc1.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase6() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\ts1.save();\n\t\tc1.save();\n\t\ts2.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase7() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tSet<Student> ss = new HashSet<>();\n\t\tss.add(s1);\n\t\tss.add(s2);\n\t\tc1.setStudentCollection(ss);\n\t\tc1.save();\n\t\ts1.save();\n\t\ts2.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase8() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tSet<Student> ss = new HashSet<>();\n\t\tss.add(s1);\n\t\tss.add(s2);\n\t\tc1.setStudentCollection(ss);\n\t\ts1.save();\n\t\ts2.save();\n\t\tc1.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase9() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tSet<Student> ss = new HashSet<>();\n\t\tss.add(s1);\n\t\tss.add(s2);\n\t\tc1.setStudentCollection(ss);\n\t\ts1.save();\n\t\tc1.save();\n\t\ts2.save();\n\t\tassertFK(c1, s1, s2);\n\t}\n\n    @Test\n\tpublic void testCase10() {\n\t\tinit();\n\t\ts1 = null;\n\t\ts2 = null;\n\t\tSet<Student> ss = new HashSet<>();\n\t\tss.add(s1);\n\t\tss.add(s2);\n\t\tc1.setStudentCollection(ss);\n\t\tc1.save();\n\t\tisDataExists(getTableName(c1), c1.get_id());\n\t\tinit();\n\t\tc1 = null;\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\ts1.save();\n\t\tisDataExists(getTableName(s1), s1.getId());\n\t\ts2.save();\n\t\tisDataExists(getTableName(s2), s2.getId());\n\t}\n\n\tprivate void assertFK(Classroom c1, Student s1, Student s2) {\n\t\tassertTrue(isFKInsertCorrect(getTableName(c1), getTableName(s1), c1.get_id(),\n\t\t\t\ts1.getId()));\n\t\tassertTrue(isFKInsertCorrect(getTableName(c1), getTableName(s2), c1.get_id(),\n\t\t\t\ts2.getId()));\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/save/Many2OneUniSaveTest.java",
    "content": "package com.litepaltest.test.crud.save;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.Teacher;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class Many2OneUniSaveTest extends LitePalTestCase {\n\n\tprivate Classroom c1;\n\n\tprivate Teacher t1;\n\n\tprivate Teacher t2;\n\n\tpublic void init() {\n\t\tc1 = new Classroom();\n\t\tc1.setName(\"Music room\");\n\t\tt1 = new Teacher();\n\t\tt1.setTeacherName(\"John\");\n\t\tt1.setAge(25);\n\t\tt2 = new Teacher();\n\t\tt2.setTeacherName(\"Sean\");\n\t\tt2.setAge(35);\n\t}\n\n    @Test\n\tpublic void testCase1() {\n\t\tinit();\n\t\tc1.getTeachers().add(t1);\n\t\tc1.getTeachers().add(t2);\n\t\tc1.save();\n\t\tt1.save();\n\t\tt2.save();\n\t\tassertFK(c1, t1, t2);\n\t}\n\n    @Test\n\tpublic void testCase2() {\n\t\tinit();\n\t\tc1.getTeachers().add(t1);\n\t\tc1.getTeachers().add(t2);\n\t\tt1.save();\n\t\tt2.save();\n\t\tc1.save();\n\t\tassertFK(c1, t1, t2);\n\t}\n\n    @Test\n\tpublic void testCase3() {\n\t\tinit();\n\t\tc1.getTeachers().add(t1);\n\t\tc1.getTeachers().add(t2);\n\t\tt1.save();\n\t\tc1.save();\n\t\tt2.save();\n\t\tassertFK(c1, t1, t2);\n\t}\n\n    @Test\n\tpublic void testCase4() {\n\t\tinit();\n\t\tt1 = null;\n\t\tt2 = null;\n\t\tc1.getTeachers().add(t1);\n\t\tc1.getTeachers().add(t2);\n\t\tc1.save();\n\t\tisDataExists(getTableName(c1), c1.get_id());\n\t}\n\n\tprivate void assertFK(Classroom c1, Teacher t1, Teacher t2) {\n\t\tassertTrue(isFKInsertCorrect(getTableName(c1), getTableName(t1), c1.get_id(),\n\t\t\t\tt1.getId()));\n\t\tassertTrue(isFKInsertCorrect(getTableName(c1), getTableName(t2), c1.get_id(),\n\t\t\t\tt2.getId()));\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/save/One2OneBiSaveTest.java",
    "content": "package com.litepaltest.test.crud.save;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.IdCard;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class One2OneBiSaveTest extends LitePalTestCase {\n\n\tprivate Student s;\n\n\tprivate IdCard i;\n\n\tprivate void init() {\n\t\ts = new Student();\n\t\ts.setName(\"Jimmy\");\n\t\ts.setAge(18);\n\t\ti = new IdCard();\n\t\ti.setNumber(\"9997777112\");\n\t\ti.setAddress(\"Nanjing road\");\n\t}\n\n    @Test\n\tpublic void testO2OBiSaveStudentFirst() {\n\t\tinit();\n\t\ts.setIdcard(i);\n\t\ti.setStudent(s);\n\t\ts.save();\n\t\ti.save();\n\t\tassertFK(s, i);\n\t}\n\n    @Test\n\tpublic void testO2OBiSaveIdCardFirst() {\n\t\tinit();\n\t\ts.setIdcard(i);\n\t\ti.setStudent(s);\n\t\ti.save();\n\t\ts.save();\n\t\tassertFK(s, i);\n\t}\n\n    @Test\n\tpublic void testO2OBiBuildNullAssocations() {\n\t\tinit();\n\t\ts.setIdcard(null);\n\t\ti.setStudent(null);\n\t\ti.save();\n\t\ts.save();\n\t\tisDataExists(getTableName(s), s.getId());\n\t\tisDataExists(getTableName(i), i.getId());\n\t}\n\n    @Test\n\tpublic void testO2OBiBuildUniAssociationsSaveStudentFirst() {\n\t\tinit();\n\t\ts.setIdcard(i);\n\t\ts.save();\n\t\ti.save();\n\t\tassertFK(s, i);\n\t}\n\n    @Test\n\tpublic void testO2OBiBuildUniAssociationsSaveIdCardFirst() {\n\t\tinit();\n\t\ts.setIdcard(i);\n\t\ti.save();\n\t\ts.save();\n\t\tassertFK(s, i);\n\t}\n\n\tprivate void assertFK(Student s, IdCard i) {\n\t\tassertTrue(isFKInsertCorrect(getTableName(s), getTableName(i), s.getId(), i.getId()));\n\t\tassertTrue(isFKInsertCorrect(getTableName(i), getTableName(s), i.getId(), s.getId()));\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/save/One2OneUniSaveTest.java",
    "content": "package com.litepaltest.test.crud.save;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.IdCard;\nimport com.litepaltest.model.Teacher;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class One2OneUniSaveTest extends LitePalTestCase {\n\n\tprivate Teacher t;\n\n\tprivate IdCard i;\n\n\tprivate void init() {\n\t\tt = new Teacher();\n\t\tt.setTeacherName(\"Will\");\n\t\tt.setTeachYears(10);\n\t\tt.setAge(40);\n\t\ti = new IdCard();\n\t\ti.setNumber(\"9997777121\");\n\t\ti.setAddress(\"shanghai road\");\n\t}\n\n    @Test\n\tpublic void testSaveIdCardFirst() {\n\t\tinit();\n\t\tt.setIdCard(i);\n\t\ti.save();\n\t\tt.save();\n\t\tassertFK(t, i);\n\t}\n\n    @Test\n\tpublic void testSaveTeacherFirst() {\n\t\tinit();\n\t\tt.setIdCard(i);\n\t\tt.save();\n\t\ti.save();\n\t\tassertFK(t, i);\n\t}\n\n    @Test\n\tpublic void testBuildNullAssociations() {\n\t\tinit();\n\t\tt.setIdCard(null);\n\t\tt.save();\n\t\ti.save();\n\t\tisDataExists(getTableName(t), t.getId());\n\t\tisDataExists(getTableName(i), i.getId());\n\t}\n\n\tprivate void assertFK(Teacher t, IdCard i) {\n\t\tassertTrue(isFKInsertCorrect(getTableName(t), getTableName(i), t.getId(), i.getId()));\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/save/SaveAllKotlinTest.kt",
    "content": "package com.litepaltest.test.crud.save\n\nimport androidx.test.filters.SmallTest\nimport com.litepaltest.model.*\nimport junit.framework.TestCase\nimport junit.framework.TestCase.assertEquals\nimport org.junit.Before\nimport org.junit.Test\nimport org.litepal.LitePal.find\nimport org.litepal.LitePal.findBySQL\nimport org.litepal.LitePal.where\nimport org.litepal.extension.saveAll\nimport org.litepal.util.DBUtility\nimport java.util.*\n\n@SmallTest\nclass SaveAllKotlinTest {\n\n    var classroomTable: String? = null\n\n    var studentTable: String? = null\n\n    @Before\n    fun setUp() {\n        classroomTable = DBUtility.getTableNameByClassName(Classroom::class.java.name)\n        studentTable = DBUtility.getTableNameByClassName(Student::class.java.name)\n    }\n\n    @Test\n    fun testSaveAll() {\n        val cellList: MutableList<Cellphone> = ArrayList()\n        for (i in 0..49) {\n            val cellPhone = Cellphone()\n            cellPhone.setBrand(\"Samsung unique\")\n            cellPhone.price = Math.random()\n            cellPhone.serial = UUID.randomUUID().toString()\n            cellList.add(cellPhone)\n        }\n        TestCase.assertTrue(cellList.saveAll())\n        for (cell in cellList) {\n            TestCase.assertTrue(cell.isSaved)\n        }\n    }\n\n    @Test\n    fun testSaveAllWithM2OOnOneSide() {\n        val classroom = Classroom()\n        classroom.name = \"Music room\"\n        for (i in 0..49) {\n            val student = Student()\n            student.name = \"Tom\"\n            student.age = Random().nextInt(20)\n            classroom.studentCollection.add(student)\n        }\n        TestCase.assertTrue(classroom.studentCollection.saveAll())\n        classroom.save()\n        val list = where(classroomTable + \"_id = ?\", classroom._id.toString()).find(Student::class.java)\n        assertEquals(50, list.size)\n    }\n\n    @Test\n    fun testSaveAllWithM2OOnManySide() {\n        val classroom = Classroom()\n        classroom.name = \"English room\"\n        val studentList: MutableList<Student> = ArrayList()\n        for (i in 0..49) {\n            val student = Student()\n            student.name = \"Tom\"\n            student.age = Random().nextInt(20)\n            student.classroom = classroom\n            studentList.add(student)\n        }\n        TestCase.assertTrue(studentList.saveAll())\n        classroom.save()\n        val list = where(classroomTable + \"_id = ?\", classroom._id.toString()).find(Student::class.java)\n        assertEquals(50, list.size)\n    }\n\n    @Test\n    fun testSaveAllWithO2O() {\n        val idcardList: MutableList<IdCard> = ArrayList()\n        val studentList: MutableList<Student> = ArrayList()\n        for (i in 0..49) {\n            val idcard = IdCard()\n            idcard.number = Random().nextInt(2000000).toString()\n            val student = Student()\n            student.name = \"Jim\"\n            student.age = Random().nextInt(20)\n            student.idcard = idcard\n            idcardList.add(idcard)\n            studentList.add(student)\n        }\n        TestCase.assertTrue(idcardList.saveAll())\n        TestCase.assertTrue(studentList.saveAll())\n        for (student in studentList) {\n            val result = where(studentTable + \"_id=?\", student.id.toString()).find(IdCard::class.java)\n            assertEquals(1, result.size)\n        }\n    }\n\n    @Test\n    fun testSaveAllWithM2M() {\n        val studentList: MutableList<Student> = ArrayList()\n        val teacherList: MutableList<Teacher> = ArrayList()\n        for (i in 0..49) {\n            val teacher = Teacher()\n            teacher.teacherName = \"Lucy\"\n            teacher.teachYears = Random().nextInt(10)\n            teacherList.add(teacher)\n        }\n        for (i in 0..49) {\n            val student = Student()\n            student.name = \"Timmy\"\n            student.age = Random().nextInt(20)\n            val index1 = Random().nextInt(50)\n            student.teachers.add(teacherList[index1])\n            var index2 = index1\n            while (index2 == index1) {\n                index2 = Random().nextInt(50)\n            }\n            student.teachers.add(teacherList[index2])\n            var index3 = index2\n            while (index3 == index2 || index3 == index1) {\n                index3 = Random().nextInt(50)\n            }\n            student.teachers.add(teacherList[index3])\n            studentList.add(student)\n        }\n        TestCase.assertTrue(studentList.saveAll())\n        TestCase.assertTrue(teacherList.saveAll())\n        val studentTable = DBUtility.getTableNameByClassName(Student::class.java.name)\n        val teacherTable = DBUtility.getTableNameByClassName(Teacher::class.java.name)\n        val tableName = DBUtility.getIntermediateTableName(studentTable, teacherTable)\n        for (student in studentList) {\n            val cursor = findBySQL(\n                    \"select * from \" + tableName + \" where \" + studentTable + \"_id=?\", student.id.toString())\n            assertEquals(3, cursor.count)\n            cursor.close()\n        }\n    }\n\n    @Test\n    fun testSaveAllGenericData() {\n        val classroomList: MutableList<Classroom> = ArrayList()\n        for (i in 0..49) {\n            val classroom = Classroom()\n            classroom.name = \"classroom $i\"\n            for (j in 0..19) {\n                classroom.news.add(\"news $i\")\n            }\n            for (k in 0..12) {\n                classroom.numbers.add(k)\n            }\n            classroomList.add(classroom)\n        }\n        TestCase.assertTrue(classroomList.saveAll())\n        assertEquals(50, classroomList.size)\n        for (classroom in classroomList) {\n            TestCase.assertTrue(classroom.isSaved)\n            val c = find(Classroom::class.java, classroom._id.toLong())\n            TestCase.assertTrue(c.name.startsWith(\"classroom\"))\n            assertEquals(20, c.news.size)\n            assertEquals(13, c.numbers.size)\n        }\n    }\n\n    @Test\n    fun testSaveAllFailed() {\n        val cellphones: MutableList<Cellphone> = ArrayList()\n        val serial = UUID.randomUUID().toString()\n        for (i in 0..19) {\n            val cellphone = Cellphone()\n            cellphone.setBrand(\"Apple\")\n            cellphone.serial = serial + i % 10 // serial is unique, so this should save failed\n            cellphones.add(cellphone)\n        }\n        TestCase.assertFalse(cellphones.saveAll())\n        val list = where(\"serial like ?\", \"$serial%\").find(Cellphone::class.java)\n        TestCase.assertTrue(list.isEmpty())\n    }\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/save/SaveAllTest.java",
    "content": "package com.litepaltest.test.crud.save;\n\nimport android.database.Cursor;\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Cellphone;\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.IdCard;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.LitePal;\nimport org.litepal.util.DBUtility;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.UUID;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class SaveAllTest {\n\n    String classroomTable;\n\n    String studentTable;\n\n    @Before\n    public void setUp() {\n        classroomTable = DBUtility.getTableNameByClassName(Classroom.class.getName());\n        studentTable = DBUtility.getTableNameByClassName(Student.class.getName());\n    }\n\n    @Test\n    public void testSaveAll() {\n\t\tList<Cellphone> cellList = new ArrayList<>();\n\t\tfor (int i = 0; i < 50; i++) {\n\t\t\tCellphone cellPhone = new Cellphone();\n\t\t\tcellPhone.setBrand(\"Samsung unique\");\n\t\t\tcellPhone.setPrice(Math.random());\n            cellPhone.setSerial(UUID.randomUUID().toString());\n\t\t\tcellList.add(cellPhone);\n\t\t}\n\t\tassertTrue(LitePal.saveAll(cellList));\n\t\tfor (Cellphone cell : cellList) {\n\t\t\tassertTrue(cell.isSaved());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testSaveAllWithM2OOnOneSide() {\n\t\tClassroom classroom = new Classroom();\n\t\tclassroom.setName(\"Music room\");\n\t\tfor (int i = 0; i < 50; i++) {\n\t\t\tStudent student = new Student();\n\t\t\tstudent.setName(\"Tom\");\n\t\t\tstudent.setAge(new Random().nextInt(20));\n\t\t\tclassroom.getStudentCollection().add(student);\n\t\t}\n\t\tassertTrue(LitePal.saveAll(classroom.getStudentCollection()));\n\t\tclassroom.save();\n\t\tList<Student> list = LitePal.where(classroomTable + \"_id = ?\",\n\t\t\t\tString.valueOf(classroom.get_id())).find(Student.class);\n\t\tassertEquals(50, list.size());\n\n\t}\n\n    @Test\n\tpublic void testSaveAllWithM2OOnManySide() {\n\t\tClassroom classroom = new Classroom();\n\t\tclassroom.setName(\"English room\");\n\t\tList<Student> studentList = new ArrayList<>();\n\t\tfor (int i = 0; i < 50; i++) {\n\t\t\tStudent student = new Student();\n\t\t\tstudent.setName(\"Tom\");\n\t\t\tstudent.setAge(new Random().nextInt(20));\n\t\t\tstudent.setClassroom(classroom);\n\t\t\tstudentList.add(student);\n\t\t}\n\t\tassertTrue(LitePal.saveAll(studentList));\n\t\tclassroom.save();\n\t\tList<Student> list = LitePal.where(classroomTable + \"_id = ?\",\n\t\t\t\tString.valueOf(classroom.get_id())).find(Student.class);\n\t\tassertEquals(50, list.size());\n\t}\n\n    @Test\n\tpublic void testSaveAllWithO2O() {\n\t\tList<IdCard> idcardList = new ArrayList<>();\n\t\tList<Student> studentList = new ArrayList<>();\n\t\tfor (int i = 0; i < 50; i++) {\n\t\t\tIdCard idcard = new IdCard();\n\t\t\tidcard.setNumber(String.valueOf(new Random().nextInt(2000000)));\n\t\t\tStudent student = new Student();\n\t\t\tstudent.setName(\"Jim\");\n\t\t\tstudent.setAge(new Random().nextInt(20));\n\t\t\tstudent.setIdcard(idcard);\n\t\t\tidcardList.add(idcard);\n\t\t\tstudentList.add(student);\n\t\t}\n\t\tassertTrue(LitePal.saveAll(idcardList));\n\t\tassertTrue(LitePal.saveAll(studentList));\n\t\tfor (Student student : studentList) {\n\t\t\tList<IdCard> result = LitePal\n\t\t\t\t\t.where(studentTable + \"_id=?\", String.valueOf(student.getId())).find(IdCard.class);\n\t\t\tassertEquals(1, result.size());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testSaveAllWithM2M() {\n\t\tList<Student> studentList = new ArrayList<>();\n\t\tList<Teacher> teacherList = new ArrayList<>();\n\t\tfor (int i = 0; i < 50; i++) {\n\t\t\tTeacher teacher = new Teacher();\n\t\t\tteacher.setTeacherName(\"Lucy\");\n\t\t\tteacher.setTeachYears(new Random().nextInt(10));\n\t\t\tteacherList.add(teacher);\n\t\t}\n\t\tfor (int i = 0; i < 50; i++) {\n\t\t\tStudent student = new Student();\n\t\t\tstudent.setName(\"Timmy\");\n\t\t\tstudent.setAge(new Random().nextInt(20));\n\t\t\tint index1 = new Random().nextInt(50);\n\t\t\tstudent.getTeachers().add(teacherList.get(index1));\n\t\t\tint index2 = index1;\n\t\t\twhile (index2 == index1) {\n\t\t\t\tindex2 = new Random().nextInt(50);\n\t\t\t}\n\t\t\tstudent.getTeachers().add(teacherList.get(index2));\n\t\t\tint index3 = index2;\n\t\t\twhile (index3 == index2 || index3 == index1) {\n\t\t\t\tindex3 = new Random().nextInt(50);\n\t\t\t}\n\t\t\tstudent.getTeachers().add(teacherList.get(index3));\n\t\t\tstudentList.add(student);\n\t\t}\n\t\tassertTrue(LitePal.saveAll(studentList));\n\t\tassertTrue(LitePal.saveAll(teacherList));\n        String studentTable = DBUtility.getTableNameByClassName(Student.class.getName());\n        String teacherTable = DBUtility.getTableNameByClassName(Teacher.class.getName());\n        String tableName = DBUtility.getIntermediateTableName(studentTable, teacherTable);\n        for (Student student : studentList) {\n\t\t\tCursor cursor = LitePal.findBySQL(\n\t\t\t\t\t\"select * from \" + tableName + \" where \" + studentTable + \"_id=?\",\n\t\t\t\t\tString.valueOf(student.getId()));\n\t\t\tassertEquals(3, cursor.getCount());\n\t\t\tcursor.close();\n\t\t}\n\t}\n\n    @Test\n    public void testSaveAllGenericData() {\n        List<Classroom> classroomList = new ArrayList<>();\n        for (int i = 0; i < 50; i++) {\n            Classroom classroom = new Classroom();\n            classroom.setName(\"classroom \" + i);\n            for (int j = 0; j < 20; j++) {\n                classroom.getNews().add(\"news \" + i);\n            }\n            for (int k = 0; k < 13; k++) {\n                classroom.getNumbers().add(k);\n            }\n            classroomList.add(classroom);\n        }\n\t\tassertTrue(LitePal.saveAll(classroomList));\n        assertEquals(50, classroomList.size());\n        for (Classroom classroom : classroomList) {\n            assertTrue(classroom.isSaved());\n            Classroom c = LitePal.find(Classroom.class, classroom.get_id());\n            assertTrue(c.getName().startsWith(\"classroom\"));\n            assertEquals(20, c.getNews().size());\n            assertEquals(13, c.getNumbers().size());\n        }\n    }\n\n    @Test\n    public void testSaveAllFailed() {\n    \tList<Cellphone> cellphones = new ArrayList<>();\n    \tString serial = UUID.randomUUID().toString();\n\t\tfor (int i = 0; i < 20; i++) {\n\t\t\tCellphone cellphone = new Cellphone();\n\t\t\tcellphone.setBrand(\"Apple\");\n\t\t\tcellphone.setSerial(serial + (i % 10)); // serial is unique, so this should save failed\n\t\t\tcellphones.add(cellphone);\n\t\t}\n\t\tassertFalse(LitePal.saveAll(cellphones));\n\t\tList<Cellphone> list = LitePal.where(\"serial like ?\", serial + \"%\").find(Cellphone.class);\n\t\tassertTrue(list.isEmpty());\n\t}\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/save/SaveTest.java",
    "content": "package com.litepaltest.test.crud.save;\n\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Book;\nimport com.litepaltest.model.Cellphone;\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.Computer;\nimport com.litepaltest.model.IdCard;\nimport com.litepaltest.model.Product;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\nimport com.litepaltest.model.WeChatMessage;\nimport com.litepaltest.model.WeiboMessage;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Test;\nimport org.litepal.LitePal;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\nimport static junit.framework.Assert.assertNotNull;\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertNull;\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class SaveTest extends LitePalTestCase {\n\n    @Test\n\tpublic void testSave() {\n\t\tCellphone cell = new Cellphone();\n\t\tcell.setBrand(\"iPhone\");\n\t\tcell.setPrice(4998.01);\n\t\tcell.setInStock('Y');\n        cell.setSerial(UUID.randomUUID().toString());\n\t\tassertTrue(cell.save());\n\t\tassertTrue(isDataExists(getTableName(cell), cell.getId()));\n\t}\n\n    @Test\n\tpublic void testSaveWithConstructors() {\n\t\tComputer computer = new Computer(\"asus\", 699.00);\n\t\tassertTrue(computer.save());\n\t\tassertTrue(isDataExists(getTableName(computer), computer.getId()));\n\t\tComputer c = getComputer(computer.getId());\n\t\tassertEquals(\"asus\", c.getBrand());\n\t\tassertEquals(699.00, c.getPrice());\n\t\tComputer cc = LitePal.find(Computer.class, computer.getId());\n\t\tassertEquals(\"asus\", cc.getBrand());\n\t\tassertEquals(699.00, cc.getPrice());\n\t\tProduct p = new Product(null);\n\t\tp.setBrand(\"apple\");\n\t\tp.setPrice(1222.33);\n\t\tp.save();\n\t}\n\n    @Test\n\tpublic void testSaveAfterDelete() {\n\t\tCellphone cell = new Cellphone();\n\t\tcell.setBrand(\"iPhone\");\n\t\tcell.setPrice(4998.01);\n\t\tcell.setInStock('Y');\n        cell.setSerial(UUID.randomUUID().toString());\n\t\tassertTrue(cell.save());\n\t\tassertTrue(isDataExists(getTableName(cell), cell.getId()));\n\t\tassertTrue(cell.delete() > 0);\n\t\tassertTrue(cell.save());\n\t\tassertTrue(isDataExists(getTableName(cell), cell.getId()));\n\t\tStudent stu = new Student();\n\t\tstu.setName(\"Jimmy\");\n\t\tIdCard idcard = new IdCard();\n\t\tidcard.setAddress(\"Washington\");\n\t\tidcard.setNumber(\"123456\");\n\t\tidcard.setStudent(stu);\n\t\tstu.setIdcard(idcard);\n\t\tstu.save();\n\t\tidcard.save();\n\t\tassertTrue(isDataExists(getTableName(stu), stu.getId()));\n\t\tassertTrue(isDataExists(getTableName(idcard), idcard.getId()));\n\t\tstu.delete();\n\t\tassertFalse(isDataExists(getTableName(stu), stu.getId()));\n\t\tassertFalse(isDataExists(getTableName(idcard), idcard.getId()));\n\t\tstu.save();\n\t\tidcard.save();\n\t\tassertTrue(isDataExists(getTableName(stu), stu.getId()));\n\t\tassertTrue(isDataExists(getTableName(idcard), idcard.getId()));\n\t\tStudent danny = new Student();\n\t\tdanny.setName(\"Danny\");\n\t\tdanny.setAge(14);\n\t\tTeacher cam = new Teacher();\n\t\tcam.setTeacherName(\"Cam\");\n\t\tcam.setAge(33);\n\t\tcam.setSex(true);\n\t\tcam.setTeachYears(5);\n\t\tTeacher jack = new Teacher();\n\t\tjack.setTeacherName(\"Jack\");\n\t\tjack.setAge(36);\n\t\tjack.setSex(false);\n\t\tjack.setTeachYears(11);\n\t\tdanny.getTeachers().add(jack);\n\t\tdanny.getTeachers().add(cam);\n\t\tcam.getStudents().add(danny);\n\t\tjack.getStudents().add(danny);\n\t\tdanny.save();\n\t\tcam.save();\n\t\tjack.save();\n\t\tassertTrue(isDataExists(getTableName(danny), danny.getId()));\n\t\tassertTrue(isDataExists(getTableName(cam), cam.getId()));\n\t\tassertTrue(isDataExists(getTableName(jack), jack.getId()));\n\t\tdanny.delete();\n\t\tassertFalse(isDataExists(getTableName(danny), danny.getId()));\n\t\tassertTrue(isDataExists(getTableName(cam), cam.getId()));\n\t\tassertTrue(isDataExists(getTableName(jack), jack.getId()));\n\t\tdanny.save();\n\t\tassertTrue(isDataExists(getTableName(danny), danny.getId()));\n\t\tassertEquals(danny.getTeachers().size(), 2);\n\t\tClassroom c = new Classroom();\n\t\tc.setName(\"test classroom\");\n\t\tStudent s = new Student();\n\t\ts.setName(\"Tom\");\n\t\ts.setClassroom(c);\n\t\tStudent s2 = new Student();\n\t\ts2.setName(\"Tom\");\n\t\ts2.setClassroom(c);\n\t\tassertTrue(c.save());\n\t\tassertTrue(s.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(isDataExists(getTableName(c), c.get_id()));\n\t\tassertTrue(isDataExists(getTableName(s), s.getId()));\n\t\tassertTrue(isDataExists(getTableName(s), s2.getId()));\n\t\tc.delete();\n\t\tassertFalse(isDataExists(getTableName(c), c.get_id()));\n\t\tassertFalse(isDataExists(getTableName(s), s.getId()));\n\t\tassertFalse(isDataExists(getTableName(s), s2.getId()));\n\t\tc.save();\n\t\ts.save();\n\t\ts2.save();\n\t\tassertTrue(isDataExists(getTableName(c), c.get_id()));\n\t\tassertTrue(isDataExists(getTableName(s), s.getId()));\n\t\tassertTrue(isDataExists(getTableName(s), s2.getId()));\n\t}\n\n    @Test\n    public void testSaveInheritModels() {\n        WeChatMessage weChatMessage = new WeChatMessage();\n        weChatMessage.setFriend(\"Tom\");\n        weChatMessage.setContent(\"Hello nice to meet you\");\n        weChatMessage.setTitle(\"Greeting message\");\n        weChatMessage.setType(1);\n        assertTrue(weChatMessage.save());\n        assertTrue(weChatMessage.getId() > 0);\n        WeChatMessage message1 = LitePal.find(WeChatMessage.class, weChatMessage.getId());\n        assertEquals(\"Tom\", message1.getFriend());\n        assertEquals(\"Hello nice to meet you\", message1.getContent());\n        assertNull(message1.getTitle());\n        assertEquals(1, message1.getType());\n\n        WeiboMessage weiboMessage = new WeiboMessage();\n        weiboMessage.setType(2);\n        weiboMessage.setTitle(\"Following message\");\n        weiboMessage.setContent(\"Something big happens\");\n        weiboMessage.setFollower(\"Jimmy\");\n        weiboMessage.setNumber(123456);\n        assertTrue(weiboMessage.save());\n        assertTrue(weiboMessage.getId() > 0);\n    }\n\n    @Test\n    public void testSaveInheritModelsWithAssociations() {\n        Cellphone cellphone = new Cellphone();\n        cellphone.setBrand(\"iPhone 7\");\n        cellphone.setInStock('N');\n        cellphone.setPrice(6999.99);\n        cellphone.setSerial(UUID.randomUUID().toString());\n        cellphone.setMac(\"ff:3d:4a:99:76\");\n        cellphone.save();\n\n        WeChatMessage weChatMessage = new WeChatMessage();\n        weChatMessage.setFriend(\"Tom\");\n        weChatMessage.setContent(\"Hello nice to meet you\");\n        weChatMessage.setTitle(\"Greeting message\");\n        weChatMessage.setType(1);\n        assertTrue(weChatMessage.save());\n        assertTrue(weChatMessage.getId() > 0);\n        WeChatMessage message1 = LitePal.find(WeChatMessage.class, weChatMessage.getId());\n        assertEquals(\"Tom\", message1.getFriend());\n        assertEquals(\"Hello nice to meet you\", message1.getContent());\n        assertNull(message1.getTitle());\n        assertEquals(1, message1.getType());\n\n        WeiboMessage weiboMessage = new WeiboMessage();\n        weiboMessage.setType(2);\n        weiboMessage.setTitle(\"Following message\");\n        weiboMessage.setContent(\"Something big happens\");\n        weiboMessage.setFollower(\"Jimmy\");\n        weiboMessage.setNumber(123456);\n        weiboMessage.setCellphone(cellphone);\n        assertTrue(weiboMessage.save());\n        assertTrue(weiboMessage.getId() > 0);\n        WeiboMessage message2 = LitePal.find(WeiboMessage.class, weiboMessage.getId(), true);\n        Cellphone result = message2.getCellphone();\n        assertEquals(cellphone.getId(), result.getId());\n        assertEquals(cellphone.getBrand(), result.getBrand());\n        assertEquals(cellphone.getInStock(), result.getInStock());\n        assertEquals(cellphone.getPrice(), result.getPrice());\n        assertEquals(cellphone.getSerial(), result.getSerial());\n        assertEquals(cellphone.getMac(), result.getMac());\n    }\n\n    @Test\n    public void testSaveGenericData() {\n        Classroom classroom = new Classroom();\n        classroom.setName(\"classroom1\");\n        classroom.getNews().add(\"news1\");\n        classroom.getNews().add(\"news2\");\n        classroom.getNews().add(\"news3\");\n        List<Integer> numbers = new ArrayList<>();\n        numbers.add(1);\n        numbers.add(2);\n        numbers.add(3);\n        numbers.add(4);\n        classroom.setNumbers(numbers);\n        classroom.save();\n        Classroom c = LitePal.find(Classroom.class, classroom.get_id());\n        assertEquals(\"classroom1\", c.getName());\n        assertEquals(3, c.getNews().size());\n        assertEquals(4, c.getNumbers().size());\n        for (String news : c.getNews()) {\n            assertTrue(news.equals(\"news1\") || news.equals(\"news2\") || news.equals(\"news3\"));\n        }\n        for (int number : c.getNumbers()) {\n            assertTrue(number == 1 || number == 2 || number == 3 || number == 4);\n        }\n    }\n\n    @Test\n    public void testSaveLongMaximumNumber() {\n    \tIdCard idCard = new IdCard();\n    \tidCard.setSerial(Long.MAX_VALUE);\n    \tidCard.setAddress(\"abczyx\");\n    \tassertTrue(idCard.save());\n    \tIdCard idCardFromDB = LitePal.find(IdCard.class, idCard.getId());\n    \tassertEquals(Long.MAX_VALUE, idCardFromDB.getSerial());\n\t}\n\n\t@Test\n\tpublic void testNullValue() {\n\t\tBook book = new Book();\n\t\tbook.setBookName(\"First Line of Android\");\n\t\tassertTrue(book.save());\n\t\tBook bookFromDB = LitePal.find(Book.class, book.getId());\n\t\tassertNotNull(bookFromDB);\n\t\tassertNull(bookFromDB.getPages()); // pages should be null cause it's Integer type and assign no value.\n\n\t\tbook.setPages(123); // assign pages\n\t\tassertTrue(book.save());\n\t\tbookFromDB = LitePal.find(Book.class, book.getId());\n\t\tassertNotNull(bookFromDB);\n\t\tassertNotNull(bookFromDB.getPages()); // now we should be pages value.\n\t\tassertEquals(Integer.valueOf(123), book.getPages());\n\t}\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/transaction/TransactionKotlinTest.kt",
    "content": "package com.litepaltest.test.crud.transaction\n\nimport android.content.ContentValues\nimport androidx.test.filters.SmallTest\nimport com.litepaltest.model.*\nimport com.litepaltest.test.LitePalTestCase\nimport junit.framework.TestCase\nimport org.junit.Assert\nimport org.junit.Test\nimport org.litepal.LitePal\nimport org.litepal.extension.find\nimport org.litepal.extension.runInTransaction\nimport java.lang.NullPointerException\nimport java.util.*\n\n@SmallTest\nclass TransactionKotlinTest : LitePalTestCase() {\n\n    @Test\n    fun testTransactionForSave() {\n        val book = Book()\n        LitePal.runInTransaction {\n            book.bookName = \"First Line of Android\"\n            book.pages = 700\n            Assert.assertTrue(book.save())\n            val bookFromDb = LitePal.find(Book::class.java, book.id)\n            Assert.assertEquals(\"First Line of Android\", bookFromDb.bookName)\n            Assert.assertEquals(700L, bookFromDb.pages.toInt().toLong())\n            false\n        }\n        Assert.assertTrue(book.isSaved)\n        val bookFromDb = LitePal.find(Book::class.java, book.id)\n        Assert.assertNull(bookFromDb)\n    }\n\n    @Test\n    fun testTransactionForSaveAll() {\n        val serial = UUID.randomUUID().toString()\n        val weiboMessage = WeiboMessage()\n        LitePal.runInTransaction {\n            weiboMessage.follower = \"nobody\"\n            val saveResult = weiboMessage.save()\n            val cellphones: MutableList<Cellphone> = ArrayList()\n            for (i in 0..19) {\n                val cellphone = Cellphone()\n                cellphone.setBrand(\"Apple\")\n                cellphone.serial = serial + i % 10 // serial is unique, so this should save failed\n                cellphone.messages.add(weiboMessage)\n                cellphones.add(cellphone)\n            }\n            val saveAllResult = LitePal.saveAll(cellphones)\n            saveResult && saveAllResult\n        }\n        Assert.assertTrue(weiboMessage.isSaved)\n        val messageFromDb = LitePal.find(WeiboMessage::class.java, weiboMessage.id.toLong())\n        Assert.assertNull(messageFromDb)\n        val list = LitePal.where(\"serial like ?\", \"$serial%\").find(Cellphone::class.java)\n        TestCase.assertTrue(list.isEmpty())\n    }\n\n    @Test\n    fun testTransactionForUpdate() {\n        val teacher = Teacher()\n        teacher.teacherName = \"Tony\"\n        teacher.teachYears = 3\n        teacher.age = 23\n        teacher.isSex = false\n        Assert.assertTrue(teacher.save())\n        LitePal.runInTransaction {\n            val values = ContentValues()\n            values.put(\"TeachYears\", 13)\n            val rows = LitePal.update(Teacher::class.java, values, teacher.id.toLong())\n            Assert.assertEquals(1, rows.toLong())\n            val teacherFromDb = LitePal.find(Teacher::class.java, teacher.id.toLong())\n            Assert.assertEquals(13, teacherFromDb.teachYears.toLong())\n            // not set transaction successful\n            false\n        }\n        val teacherFromDb = LitePal.find(Teacher::class.java, teacher.id.toLong())\n        Assert.assertEquals(3, teacherFromDb.teachYears.toLong())\n    }\n\n    @Test\n    fun testTransactionForDelete() {\n        val tony = Student()\n        tony.name = \"Tony\"\n        tony.age = 23\n        tony.save()\n        val studentId = tony.id\n        LitePal.runInTransaction {\n            val rowsAffected = tony.delete()\n            Assert.assertEquals(1, rowsAffected.toLong())\n            val studentFromDb = LitePal.find<Student>(studentId.toLong())\n            Assert.assertNull(studentFromDb)\n            // not set transaction successful\n            false\n        }\n        val studentFromDb = LitePal.find<Student>(studentId.toLong())\n        Assert.assertNotNull(studentFromDb)\n        Assert.assertEquals(\"Tony\", studentFromDb!!.name)\n        Assert.assertEquals(23, studentFromDb.age.toLong())\n    }\n\n    @Test\n    fun testTransactionForCRUD() {\n        var lastId = -1\n        LitePal.runInTransaction {\n            val tony = Student()\n            tony.name = \"Tony\"\n            tony.age = 23\n            tony.save()\n            val studentId = tony.id\n            var studentFromDb = LitePal.find<Student>(studentId.toLong())\n            Assert.assertNotNull(studentFromDb)\n            Assert.assertEquals(\"Tony\", studentFromDb!!.name)\n            Assert.assertEquals(23, studentFromDb.age.toLong())\n            val updateModel = Student()\n            updateModel.age = 25\n            var rowsAffected = updateModel.update(studentId.toLong())\n            Assert.assertEquals(1, rowsAffected.toLong())\n            studentFromDb = LitePal.find(Student::class.java, studentId.toLong())\n            Assert.assertEquals(25, studentFromDb.age.toLong())\n            rowsAffected = tony.delete()\n            Assert.assertEquals(1, rowsAffected.toLong())\n            studentFromDb = LitePal.find(Student::class.java, studentId.toLong())\n            Assert.assertNull(studentFromDb)\n            Assert.assertTrue(tony.save())\n            studentFromDb = LitePal.find(Student::class.java, tony.id.toLong())\n            Assert.assertNotNull(studentFromDb)\n            lastId = tony.id\n            // not set transaction successful\n            false\n        }\n        val studentFromDb = LitePal.find<Student>(lastId.toLong())\n        Assert.assertNull(studentFromDb)\n    }\n\n    @Test\n    fun testTransactionSuccessfulForCRUD() {\n        var lastId = -1\n        LitePal.runInTransaction {\n            val tony = Student()\n            tony.name = \"Tony\"\n            tony.age = 23\n            tony.save()\n            val studentId = tony.id\n            var studentFromDb = LitePal.find<Student>(studentId.toLong())\n            Assert.assertNotNull(studentFromDb)\n            Assert.assertEquals(\"Tony\", studentFromDb!!.name)\n            Assert.assertEquals(23, studentFromDb.age.toLong())\n            val updateModel = Student()\n            updateModel.age = 25\n            var rowsAffected = updateModel.update(studentId.toLong())\n            Assert.assertEquals(1, rowsAffected.toLong())\n            studentFromDb = LitePal.find(Student::class.java, studentId.toLong())\n            Assert.assertEquals(25, studentFromDb.age.toLong())\n            rowsAffected = tony.delete()\n            Assert.assertEquals(1, rowsAffected.toLong())\n            studentFromDb = LitePal.find(Student::class.java, studentId.toLong())\n            Assert.assertNull(studentFromDb)\n            Assert.assertTrue(tony.save())\n            studentFromDb = LitePal.find(Student::class.java, tony.id.toLong())\n            Assert.assertNotNull(studentFromDb)\n            lastId = tony.id\n            // not set transaction successful\n            true\n        }\n        val studentFromDb = LitePal.find<Student>(lastId.toLong())\n        Assert.assertNotNull(studentFromDb)\n        Assert.assertEquals(\"Tony\", studentFromDb!!.name)\n        Assert.assertEquals(23, studentFromDb.age.toLong())\n    }\n\n    @Test\n    fun testTransactionWithException() {\n        val book = Book()\n        LitePal.runInTransaction {\n            book.bookName = \"First Line of Android\"\n            book.pages = 700\n            Assert.assertTrue(book.save())\n            val bookFromDb = LitePal.find(Book::class.java, book.id)\n            Assert.assertEquals(\"First Line of Android\", bookFromDb.bookName)\n            Assert.assertEquals(700L, bookFromDb.pages.toInt().toLong())\n            if (true) throw NullPointerException(\"just throw to fail the transaction\")\n            true\n        }\n        Assert.assertTrue(book.isSaved)\n        val bookFromDb = LitePal.find(Book::class.java, book.id)\n        Assert.assertNull(bookFromDb)\n    }\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/transaction/TransactionTest.java",
    "content": "package com.litepaltest.test.crud.transaction;\n\nimport android.content.ContentValues;\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Book;\nimport com.litepaltest.model.Cellphone;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\nimport com.litepaltest.model.WeiboMessage;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.litepal.LitePal;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class TransactionTest extends LitePalTestCase {\n\n    @Test\n    public void testTransactionForSave() {\n        LitePal.beginTransaction();\n        Book book = new Book();\n        try {\n            book.setBookName(\"First Line of Android\");\n            book.setPages(700);\n            Assert.assertTrue(book.save());\n            Book bookFromDb = LitePal.find(Book.class, book.getId());\n            Assert.assertEquals(\"First Line of Android\", bookFromDb.getBookName());\n            Assert.assertEquals(700L, bookFromDb.getPages().intValue());\n            if (true) {\n                throw new NullPointerException(\"Throw a exception to fail the transaction.\");\n            }\n            LitePal.setTransactionSuccessful();\n        } catch (Exception e) {\n            // do nothing\n        } finally {\n            LitePal.endTransaction();\n        }\n        Assert.assertTrue(book.isSaved());\n        Book bookFromDb = LitePal.find(Book.class, book.getId());\n        Assert.assertNull(bookFromDb);\n    }\n\n    @Test\n    public void testTransactionForSaveAll() {\n        LitePal.beginTransaction();\n        String serial = UUID.randomUUID().toString();\n        WeiboMessage weiboMessage = new WeiboMessage();\n        try {\n            weiboMessage.setFollower(\"nobody\");\n            boolean saveResult = weiboMessage.save();\n            List<Cellphone> cellphones = new ArrayList<>();\n            for (int i = 0; i < 20; i++) {\n                Cellphone cellphone = new Cellphone();\n                cellphone.setBrand(\"Apple\");\n                cellphone.setSerial(serial + (i % 10)); // serial is unique, so this should save failed\n                cellphone.getMessages().add(weiboMessage);\n                cellphones.add(cellphone);\n            }\n            boolean saveAllResult = LitePal.saveAll(cellphones);\n            if (saveResult && saveAllResult) {\n                LitePal.setTransactionSuccessful();\n            }\n        } finally {\n            LitePal.endTransaction();\n        }\n        Assert.assertTrue(weiboMessage.isSaved());\n        WeiboMessage messageFromDb = LitePal.find(WeiboMessage.class, weiboMessage.getId());\n        Assert.assertNull(messageFromDb);\n        List<Cellphone> list = LitePal.where(\"serial like ?\", serial + \"%\").find(Cellphone.class);\n        assertTrue(list.isEmpty());\n    }\n\n    @Test\n    public void testTransactionForUpdate() {\n        Teacher teacher = new Teacher();\n        teacher.setTeacherName(\"Tony\");\n        teacher.setTeachYears(3);\n        teacher.setAge(23);\n        teacher.setSex(false);\n        Assert.assertTrue(teacher.save());\n        LitePal.beginTransaction();\n        ContentValues values = new ContentValues();\n        values.put(\"TeachYears\", 13);\n        int rows = LitePal.update(Teacher.class, values, teacher.getId());\n        Assert.assertEquals(1, rows);\n        Teacher teacherFromDb = LitePal.find(Teacher.class, teacher.getId());\n        Assert.assertEquals(13, teacherFromDb.getTeachYears());\n        // not set transaction successful\n        LitePal.endTransaction();\n        teacherFromDb = LitePal.find(Teacher.class, teacher.getId());\n        Assert.assertEquals(3, teacherFromDb.getTeachYears());\n    }\n\n    @Test\n    public void testTransactionForDelete() {\n        Student tony = new Student();\n        tony.setName(\"Tony\");\n        tony.setAge(23);\n        tony.save();\n        int studentId = tony.getId();\n        LitePal.beginTransaction();\n        int rowsAffected = tony.delete();\n        Assert.assertEquals(1, rowsAffected);\n        Student studentFromDb = LitePal.find(Student.class, studentId);\n        Assert.assertNull(studentFromDb);\n        // not set transaction successful\n        LitePal.endTransaction();\n        studentFromDb = LitePal.find(Student.class, studentId);\n        Assert.assertNotNull(studentFromDb);\n        Assert.assertEquals(\"Tony\", studentFromDb.getName());\n        Assert.assertEquals(23, studentFromDb.getAge());\n    }\n\n    @Test\n    public void testTransactionForCRUD() {\n        LitePal.beginTransaction();\n        Student tony = new Student();\n        tony.setName(\"Tony\");\n        tony.setAge(23);\n        tony.save();\n        int studentId = tony.getId();\n        Student studentFromDb = LitePal.find(Student.class, studentId);\n        Assert.assertNotNull(studentFromDb);\n        Assert.assertEquals(\"Tony\", studentFromDb.getName());\n        Assert.assertEquals(23, studentFromDb.getAge());\n        Student updateModel = new Student();\n        updateModel.setAge(25);\n        int rowsAffected = updateModel.update(studentId);\n        Assert.assertEquals(1, rowsAffected);\n        studentFromDb = LitePal.find(Student.class, studentId);\n        Assert.assertEquals(25, studentFromDb.getAge());\n        rowsAffected = tony.delete();\n        Assert.assertEquals(1, rowsAffected);\n        studentFromDb = LitePal.find(Student.class, studentId);\n        Assert.assertNull(studentFromDb);\n        Assert.assertTrue(tony.save());\n        studentFromDb = LitePal.find(Student.class, tony.getId());\n        Assert.assertNotNull(studentFromDb);\n        // not set transaction successful\n        LitePal.endTransaction();\n        studentFromDb = LitePal.find(Student.class, tony.getId());\n        Assert.assertNull(studentFromDb);\n    }\n\n    @Test\n    public void testTransactionSuccessfulForCRUD() {\n        LitePal.beginTransaction();\n        Student tony = new Student();\n        tony.setName(\"Tony\");\n        tony.setAge(23);\n        tony.save();\n        int studentId = tony.getId();\n        Student studentFromDb = LitePal.find(Student.class, studentId);\n        Assert.assertNotNull(studentFromDb);\n        Assert.assertEquals(\"Tony\", studentFromDb.getName());\n        Assert.assertEquals(23, studentFromDb.getAge());\n        Student updateModel = new Student();\n        updateModel.setAge(25);\n        int rowsAffected = updateModel.update(studentId);\n        Assert.assertEquals(1, rowsAffected);\n        studentFromDb = LitePal.find(Student.class, studentId);\n        Assert.assertEquals(25, studentFromDb.getAge());\n        rowsAffected = tony.delete();\n        Assert.assertEquals(1, rowsAffected);\n        studentFromDb = LitePal.find(Student.class, studentId);\n        Assert.assertNull(studentFromDb);\n        Assert.assertTrue(tony.save());\n        studentFromDb = LitePal.find(Student.class, tony.getId());\n        Assert.assertNotNull(studentFromDb);\n        LitePal.setTransactionSuccessful();\n        LitePal.endTransaction();\n        studentFromDb = LitePal.find(Student.class, tony.getId());\n        Assert.assertNotNull(studentFromDb);\n        Assert.assertEquals(\"Tony\", studentFromDb.getName());\n        Assert.assertEquals(23, studentFromDb.getAge());\n    }\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/update/UpdateUsingSaveMethodTest.java",
    "content": "package com.litepaltest.test.crud.update;\n\nimport androidx.test.filters.SmallTest;\n\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.UUID;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.LitePal;\nimport org.litepal.util.DBUtility;\n\nimport com.litepaltest.model.Cellphone;\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.IdCard;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class UpdateUsingSaveMethodTest extends LitePalTestCase {\n    \n    String classroomTable;\n    \n    String studentTable;\n    \n    String teacherTable;\n    \n    String idcardTable;\n\n\tprivate Classroom c1;\n\n\tprivate Classroom c2;\n\n\tprivate Student s1;\n\n\tprivate Student s2;\n\n\tprivate Student s3;\n\n\tprivate IdCard id1;\n\n\tprivate Teacher t1;\n\n\tprivate Teacher t2;\n\n    @Before\n    public void setUp() {\n        classroomTable = DBUtility.getTableNameByClassName(Classroom.class.getName());\n        studentTable = DBUtility.getTableNameByClassName(Student.class.getName());\n        teacherTable = DBUtility.getTableNameByClassName(Teacher.class.getName());\n        idcardTable = DBUtility.getTableNameByClassName(IdCard.class.getName());\n    }\n\n    private void init() {\n\t\tCalendar calendar = Calendar.getInstance();\n\t\tc1 = new Classroom();\n\t\tc1.setName(\"Working room\");\n\t\tc2 = new Classroom();\n\t\tc2.setName(\"Resting room\");\n\t\ts1 = new Student();\n\t\ts1.setName(\"Parker\");\n\t\ts1.setAge(18);\n\t\ts2 = new Student();\n\t\ts2.setName(\"Peter\");\n\t\tcalendar.clear();\n\t\tcalendar.set(1990, 9, 16, 0, 0, 0);\n\t\ts2.setBirthday(calendar.getTime());\n\t\ts2.setAge(19);\n\t\ts3 = new Student();\n\t\ts3.setName(\"Miley\");\n\t\ts3.setAge(16);\n\t\tid1 = new IdCard();\n\t\tid1.setNumber(\"999777123\");\n\t\tid1.setAddress(\"Zhushan road\");\n\t\tt1 = new Teacher();\n\t\tt1.setTeacherName(\"Jackson\");\n\t\tt1.setTeachYears(3);\n\t\tt1.setAge(28);\n\t\tt2 = new Teacher();\n\t\tt2.setTeacherName(\"Rose\");\n\t\tt2.setTeachYears(12);\n\t\tt2.setAge(34);\n\t}\n\n    @Test\n\tpublic void testUpdateBasicValues() {\n\t\tCellphone cell = new Cellphone();\n\t\tcell.setBrand(\"SamSung\");\n\t\tcell.setPrice(3988.12);\n\t\tcell.setInStock('Y');\n        cell.setSerial(UUID.randomUUID().toString());\n\t\tassertTrue(cell.save());\n\t\tassertTrue(isDataExists(getTableName(cell), cell.getId()));\n\t\t// reduce price, sold out.\n\t\tcell.setPrice(2899.88);\n\t\tcell.setInStock('N');\n\t\tassertTrue(cell.save());\n\t\tCellphone updatedCell = getCellPhone(cell.getId());\n\t\tassertEquals(2899.88, updatedCell.getPrice());\n\t\tassertEquals('N', (char) updatedCell.getInStock());\n\t}\n\n    @Test\n    public void testUpdateGenericData() {\n        Classroom classroom = new Classroom();\n        classroom.setName(\"Classroom origin\");\n        classroom.getNews().add(\"n\");\n        classroom.getNews().add(\"e\");\n        classroom.getNews().add(\"w\");\n        classroom.getNumbers().add(1);\n        classroom.getNumbers().add(2);\n        classroom.getNumbers().add(3);\n        classroom.save();\n        classroom.setName(\"Classroom update\");\n        classroom.getNews().add(\"s\");\n        classroom.getNumbers().clear();\n        classroom.save();\n        Classroom c = LitePal.find(Classroom.class, classroom.get_id());\n        assertEquals(\"Classroom update\", c.getName());\n        assertEquals(4, classroom.getNews().size());\n        assertEquals(0, classroom.getNumbers().size());\n        StringBuilder builder = new StringBuilder();\n        for (String s : classroom.getNews()) {\n            builder.append(s);\n        }\n        assertEquals(\"news\", builder.toString());\n    }\n\n    @Test\n\tpublic void testUpdateM2OAssociationsOnMSide() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tassertTrue(c1.save());\n\t\tassertTrue(c2.save());\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\ts1.setClassroom(c2);\n\t\ts2.setClassroom(c2);\n\t\tCalendar calendar = Calendar.getInstance();\n\t\tcalendar.clear();\n\t\tcalendar.set(1989, 7, 7, 0, 0, 0);\n\t\ts2.setBirthday(calendar.getTime());\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertEquals(c2.get_id(), getForeignKeyValue(studentTable, classroomTable, s1.getId()));\n\t\tassertEquals(c2.get_id(), getForeignKeyValue(studentTable, classroomTable, s2.getId()));\n\t\tStudent student2 = LitePal.find(Student.class, s2.getId());\n\t\tcalendar.clear();\n\t\tcalendar.set(1989, 7, 7, 0, 0, 0);\n\t\tassertEquals(calendar.getTimeInMillis(), student2.getBirthday().getTime());\n\t}\n\n    @Test\n\tpublic void testUpdateM2OAssociationsOnOSide() {\n\t\tinit();\n\t\tc1.getStudentCollection().add(s1);\n\t\tc1.getStudentCollection().add(s2);\n\t\tassertTrue(c1.save());\n\t\tassertTrue(c2.save());\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tc2.getStudentCollection().add(s1);\n\t\tc2.getStudentCollection().add(s2);\n\t\tassertTrue(c2.save());\n\t\tassertEquals(c2.get_id(), getForeignKeyValue(studentTable, classroomTable, s1.getId()));\n\t\tassertEquals(c2.get_id(), getForeignKeyValue(studentTable, classroomTable, s2.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2OAssociationsOnMSideWithNotSavedModel() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tassertTrue(c1.save());\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\ts1.setClassroom(c2);\n\t\ts2.setClassroom(c2);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertEquals(c1.get_id(), getForeignKeyValue(studentTable, classroomTable, s1.getId()));\n\t\tassertEquals(c1.get_id(), getForeignKeyValue(studentTable, classroomTable, s2.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2OAssociationsOnOSideWithNotSavedModel() {\n\t\tinit();\n\t\tc1.getStudentCollection().add(s1);\n\t\tc1.getStudentCollection().add(s2);\n\t\tassertTrue(c1.save());\n\t\tassertTrue(c2.save());\n\t\tassertTrue(s1.save());\n\t\tc2.getStudentCollection().add(s1);\n\t\tc2.getStudentCollection().add(s2);\n\t\tassertTrue(c2.save());\n\t\tassertEquals(c2.get_id(), getForeignKeyValue(studentTable, classroomTable, s1.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2OAssociationsOnMSideWithNull() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tassertTrue(c1.save());\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\ts1.setClassroom(null);\n\t\ts2.setClassroom(null);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertEquals(0, getForeignKeyValue(studentTable, classroomTable, s1.getId()));\n\t\tassertEquals(0, getForeignKeyValue(studentTable, classroomTable, s2.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2OAssociationsOnOSideWithNull() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tassertTrue(c1.save());\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tc1.setStudentCollection(null);\n\t\tassertTrue(c1.save());\n\t\tassertEquals(0, getForeignKeyValue(studentTable, classroomTable, s1.getId()));\n\t\tassertEquals(0, getForeignKeyValue(studentTable, classroomTable, s2.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2OAssociationsOnOSideWithEmptyCollection() {\n\t\tinit();\n\t\ts1.setClassroom(c1);\n\t\ts2.setClassroom(c1);\n\t\tassertTrue(c1.save());\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tc1.getStudentCollection().clear();\n\t\tassertTrue(c1.save());\n\t\tassertEquals(0, getForeignKeyValue(studentTable, classroomTable, s1.getId()));\n\t\tassertEquals(0, getForeignKeyValue(studentTable, classroomTable, s2.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateO2OAssociations() {\n\t\tinit();\n\t\tassertTrue(s3.save());\n\t\tassertTrue(id1.save());\n\t\ts3.setIdcard(id1);\n\t\tid1.setStudent(s3);\n\t\tassertTrue(s3.save());\n\t\tassertTrue(id1.save());\n\t\tassertEquals(s3.getId(), getForeignKeyValue(idcardTable, studentTable, id1.getId()));\n\t\tassertEquals(id1.getId(), getForeignKeyValue(studentTable, idcardTable, s3.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateO2OAssociationsWithNull() {\n\t\tinit();\n\t\ts3.setIdcard(id1);\n\t\tid1.setStudent(s3);\n\t\tassertTrue(s3.save());\n\t\tassertTrue(id1.save());\n\t\ts3.setIdcard(null);\n\t\tid1.setStudent(null);\n\t\tassertTrue(s3.save());\n\t\tassertTrue(id1.save());\n\t\tassertEquals(0, getForeignKeyValue(idcardTable, studentTable, id1.getId()));\n\t\tassertEquals(0, getForeignKeyValue(studentTable, idcardTable, s3.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2MAssociations() {\n\t\tinit();\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(s3.save());\n\t\tassertTrue(t1.save());\n\t\tassertTrue(t2.save());\n\t\tList<Teacher> teachers = new LinkedList<>();\n\t\tteachers.add(t1);\n\t\tteachers.add(t2);\n\t\ts1.setTeachers(teachers);\n\t\ts2.setTeachers(teachers);\n\t\ts3.setTeachers(teachers);\n\t\tList<Student> students = new ArrayList<>();\n\t\tstudents.add(s1);\n\t\tstudents.add(s2);\n\t\tstudents.add(s3);\n\t\tt1.setStudents(students);\n\t\tt2.setStudents(students);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(s3.save());\n\t\tassertTrue(t1.save());\n\t\tassertTrue(t2.save());\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s1), getTableName(t1), s1.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s2), getTableName(t1), s2.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s3), getTableName(t1), s3.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s1), getTableName(t2), s1.getId(),\n\t\t\t\tt2.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s2), getTableName(t2), s2.getId(),\n\t\t\t\tt2.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s3), getTableName(t2), s3.getId(),\n\t\t\t\tt2.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2MAssociationsWithNull() {\n\t\tinit();\n\t\tList<Teacher> teachers = new LinkedList<>();\n\t\tteachers.add(t1);\n\t\tteachers.add(t2);\n\t\ts1.setTeachers(teachers);\n\t\ts2.setTeachers(teachers);\n\t\ts3.setTeachers(teachers);\n\t\tList<Student> students = new ArrayList<>();\n\t\tstudents.add(s1);\n\t\tstudents.add(s2);\n\t\tstudents.add(s3);\n\t\tt1.setStudents(students);\n\t\tt2.setStudents(students);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(s3.save());\n\t\tassertTrue(t1.save());\n\t\tassertTrue(t2.save());\n\t\ts1.setTeachers(null);\n\t\ts2.setTeachers(null);\n\t\ts3.setTeachers(null);\n\t\tt1.setStudents(null);\n\t\tt2.setStudents(null);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(s3.save());\n\t\tassertTrue(t1.save());\n\t\tassertTrue(t2.save());\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s1), getTableName(t1), s1.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s2), getTableName(t1), s2.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s3), getTableName(t1), s3.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s1), getTableName(t2), s1.getId(),\n\t\t\t\tt2.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s2), getTableName(t2), s2.getId(),\n\t\t\t\tt2.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s3), getTableName(t2), s3.getId(),\n\t\t\t\tt2.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2MAssociationsWithRefreshedCollection() {\n\t\tinit();\n\t\tList<Teacher> teachers = new LinkedList<>();\n\t\tteachers.add(t1);\n\t\tteachers.add(t2);\n\t\ts1.setTeachers(teachers);\n\t\ts2.setTeachers(teachers);\n\t\ts3.setTeachers(teachers);\n\t\tList<Student> students = new ArrayList<>();\n\t\tstudents.add(s1);\n\t\tstudents.add(s2);\n\t\tstudents.add(s3);\n\t\tt1.setStudents(students);\n\t\tt2.setStudents(students);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(s3.save());\n\t\tassertTrue(t1.save());\n\t\tassertTrue(t2.save());\n\t\tteachers.clear();\n\t\tteachers.add(t2);\n\t\tstudents.clear();\n\t\tstudents.add(s3);\n\t\ts1.setTeachers(teachers);\n\t\ts2.setTeachers(teachers);\n\t\ts3.setTeachers(teachers);\n\t\tt1.setStudents(students);\n\t\tt2.setStudents(students);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(s3.save());\n\t\tassertTrue(t1.save());\n\t\tassertTrue(t2.save());\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s1), getTableName(t1), s1.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s2), getTableName(t1), s2.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s3), getTableName(t1), s3.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s1), getTableName(t2), s1.getId(),\n\t\t\t\tt2.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s2), getTableName(t2), s2.getId(),\n\t\t\t\tt2.getId()));\n\t\tassertTrue(isIntermediateDataCorrect(getTableName(s3), getTableName(t2), s3.getId(),\n\t\t\t\tt2.getId()));\n\t}\n\n    @Test\n\tpublic void testUpdateM2MAssociationsWithEmptyCollection() {\n\t\tinit();\n\t\tList<Teacher> teachers = new LinkedList<>();\n\t\tteachers.add(t1);\n\t\tteachers.add(t2);\n\t\ts1.setTeachers(teachers);\n\t\ts2.setTeachers(teachers);\n\t\ts3.setTeachers(teachers);\n\t\tList<Student> students = new ArrayList<>();\n\t\tstudents.add(s1);\n\t\tstudents.add(s2);\n\t\tstudents.add(s3);\n\t\tt1.setStudents(students);\n\t\tt2.setStudents(students);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(s3.save());\n\t\tassertTrue(t1.save());\n\t\tassertTrue(t2.save());\n\t\tteachers.clear();\n\t\tstudents.clear();\n\t\ts1.setTeachers(teachers);\n\t\ts2.setTeachers(teachers);\n\t\ts3.setTeachers(teachers);\n\t\tt1.setStudents(students);\n\t\tt2.setStudents(students);\n\t\tassertTrue(s1.save());\n\t\tassertTrue(s2.save());\n\t\tassertTrue(s3.save());\n\t\tassertTrue(t1.save());\n\t\tassertTrue(t2.save());\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s1), getTableName(t1), s1.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s2), getTableName(t1), s2.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s3), getTableName(t1), s3.getId(),\n\t\t\t\tt1.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s1), getTableName(t2), s1.getId(),\n\t\t\t\tt2.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s2), getTableName(t2), s2.getId(),\n\t\t\t\tt2.getId()));\n\t\tassertFalse(isIntermediateDataCorrect(getTableName(s3), getTableName(t2), s3.getId(),\n\t\t\t\tt2.getId()));\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/update/UpdateUsingUpdateMethodKotlinTest.kt",
    "content": "package com.litepaltest.test.crud.update\n\nimport android.content.ContentValues\nimport android.database.sqlite.SQLiteException\nimport androidx.test.filters.SmallTest\nimport com.litepaltest.model.*\nimport com.litepaltest.test.LitePalTestCase\nimport junit.framework.TestCase.*\nimport org.junit.Before\nimport org.junit.Test\nimport org.litepal.LitePal\nimport org.litepal.exceptions.DataSupportException\nimport org.litepal.extension.find\nimport org.litepal.extension.update\nimport org.litepal.extension.updateAll\nimport org.litepal.tablemanager.Connector\nimport org.litepal.util.DBUtility\nimport java.util.*\n\n@SmallTest\nclass UpdateUsingUpdateMethodKotlinTest : LitePalTestCase() {\n\n    private var teacher: Teacher? = null\n\n    private var student: Student? = null\n\n    private var classroom: Classroom? = null\n\n    private var studentTable: String? = null\n\n    @Before\n    fun setUp() {\n        studentTable = DBUtility.getTableNameByClassName(Student::class.java.name)\n        init()\n    }\n\n    private fun init() {\n        classroom = Classroom()\n        classroom!!.name = \"English room\"\n        classroom!!.news.add(\"hello\")\n        classroom!!.news.add(\"world\")\n        classroom!!.numbers.add(123)\n        classroom!!.numbers.add(456)\n        teacher = Teacher()\n        teacher!!.teacherName = \"Tony\"\n        teacher!!.teachYears = 3\n        teacher!!.age = 23\n        teacher!!.isSex = false\n        student = Student()\n        student!!.name = \"Jonny\"\n        student!!.age = 13\n        student!!.classroom = classroom\n        student!!.birthday = Date()\n        student!!.teachers.add(teacher)\n        teacher!!.students.add(student)\n        student!!.save()\n        teacher!!.save()\n        classroom!!.save()\n    }\n\n    @Test\n    fun testUpdateWithStaticUpdate() {\n        val values = ContentValues()\n        values.put(\"TEACHERNAME\", \"Toy\")\n        var rowsAffected = LitePal.update<Teacher>(values, teacher!!.id.toLong())\n        assertEquals(1, rowsAffected)\n        assertEquals(\"Toy\", getTeacher(teacher!!.id.toLong())!!.teacherName)\n        values.clear()\n        values.put(\"aGe\", 15)\n        rowsAffected = LitePal.update<Student>(values, student!!.id.toLong())\n        assertEquals(1, rowsAffected)\n        assertEquals(15, getStudent(student!!.id.toLong())!!.age)\n    }\n\n    @Test\n    fun testUpdateWithStaticUpdateButWrongClass() {\n        val values = ContentValues()\n        values.put(\"TEACHERNAME\", \"Toy\")\n        try {\n            LitePal.update<Any>(values, teacher!!.id.toLong())\n        } catch (e: SQLiteException) {\n        }\n\n    }\n\n    @Test\n    fun testUpdateWithStaticUpdateButWrongColumn() {\n        val values = ContentValues()\n        values.put(\"TEACHERYEARS\", 13)\n        try {\n            LitePal.update(Teacher::class.java, values, teacher!!.id.toLong())\n            fail(\"no such column: TEACHERYEARS\")\n        } catch (e: SQLiteException) {\n        }\n\n    }\n\n    @Test\n    fun testUpdateWithStaticUpdateButNotExistsRecord() {\n        val values = ContentValues()\n        values.put(\"TEACHERNAME\", \"Toy\")\n        val rowsAffected = LitePal.update(Teacher::class.java, values, 998909)\n        assertEquals(0, rowsAffected)\n    }\n\n    @Test\n    fun testUpdateWithInstanceUpdate() {\n        val t = Teacher()\n        t.age = 66\n        t.teacherName = \"Jobs\"\n        t.teachYears = 33\n        t.isSex = false\n        val rowsAffected = t.update(teacher!!.id.toLong())\n        assertEquals(1, rowsAffected)\n        val newTeacher = getTeacher(teacher!!.id.toLong())\n        assertEquals(\"Jobs\", newTeacher!!.teacherName)\n        assertEquals(33, newTeacher.teachYears)\n        assertEquals(66, newTeacher.age)\n    }\n\n    @Test\n    fun testUpdateWithDefaultValueWithInstanceUpdate() {\n        val t = Teacher()\n        t.teacherName = \"\"\n        t.teachYears = 0\n        t.isSex = true\n        t.age = 22\n        val affectedTeacher = t.update(teacher!!.id.toLong())\n        assertEquals(0, affectedTeacher)\n        val newTeacher = getTeacher(teacher!!.id.toLong())\n        assertEquals(teacher!!.age, newTeacher!!.age)\n        assertEquals(teacher!!.teacherName, newTeacher.teacherName)\n        assertEquals(teacher!!.teachYears, newTeacher.teachYears)\n        assertEquals(teacher!!.isSex, newTeacher.isSex)\n        val s = Student()\n        s.name = null\n        s.age = 0\n        val affectedStudent = s.update(student!!.id.toLong())\n        assertEquals(0, affectedStudent)\n        val newStudent = getStudent(student!!.id.toLong())\n        assertEquals(student!!.name, newStudent!!.name)\n        assertEquals(student!!.age, newStudent.age)\n    }\n\n    @Test\n    fun testUpdateToDefaultValueWithInstanceUpdate() {\n        val s = Student()\n        s.setToDefault(\"age\")\n        s.setToDefault(\"name\")\n        s.setToDefault(\"birthday\")\n        val affectedStudent = s.update(student!!.id.toLong())\n        assertEquals(1, affectedStudent)\n        val newStudent = LitePal.find<Student>(student!!.id.toLong())\n        assertNull(newStudent!!.birthday)\n        assertNull(newStudent.name)\n        assertEquals(0, newStudent.age)\n        val t = Teacher()\n        t.age = 45\n        t.teachYears = 5\n        t.teacherName = \"John\"\n        t.setToDefault(\"teacherName\")\n        t.setToDefault(\"age\")\n        val affectedTeacher = t.update(teacher!!.id.toLong())\n        assertEquals(1, affectedTeacher)\n        val newTeacher = getTeacher(teacher!!.id.toLong())\n        assertEquals(22, newTeacher!!.age)\n        assertEquals(\"\", newTeacher.teacherName)\n        assertEquals(5, newTeacher.teachYears)\n    }\n\n    @Test\n    fun testUpdateToDefaultValueWithInstanceUpdateButWrongField() {\n        try {\n            val t = Teacher()\n            t.setToDefault(\"name\")\n            t.update(t.id.toLong())\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\n                    \"The name field in com.litepaltest.model.Teacher class is necessary which does not exist.\",\n                    e.message)\n        }\n\n    }\n\n    @Test\n    fun testUpdateWithInstanceUpdateWithConstructor() {\n        try {\n            val computer = Computer(\"ACER\", 5444.0)\n            computer.save()\n            computer.update(computer.id)\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\"com.litepaltest.model.Computer needs a default constructor.\",\n                    e.message)\n        }\n\n    }\n\n    @Test\n    fun testUpdateWithInstanceUpdateButNotExistsRecord() {\n        val t = Teacher()\n        t.teacherName = \"Johnny\"\n        val rowsAffected = t.update(189876465)\n        assertEquals(0, rowsAffected)\n    }\n\n    @Test\n    fun testUpdateAllWithStaticUpdate() {\n        var s: Student\n        val ids = IntArray(5)\n        for (i in 0..4) {\n            s = Student()\n            s.name = \"Dusting\"\n            s.age = i + 10\n            s.save()\n            ids[i] = s.id\n        }\n        val values = ContentValues()\n        values.put(\"age\", 24)\n        var affectedRows = LitePal.updateAll<Student>(values, \"name = ? and age = ?\",\n                \"Dusting\", \"13\")\n        assertEquals(1, affectedRows)\n        val updatedStu = getStudent(ids[3].toLong())\n        assertEquals(24, updatedStu!!.age)\n        values.clear()\n        values.put(\"name\", \"Dustee\")\n        affectedRows = LitePal.updateAll<Student>(values, \"name = ?\", \"Dusting\")\n        assertEquals(5, affectedRows)\n        val students = getStudents(ids)\n        for (updatedStudent in students) {\n            assertEquals(\"Dustee\", updatedStudent.name)\n        }\n    }\n\n    @Test\n    fun testUpdateAllRowsWithStaticUpdate() {\n        var allRows = getRowsCount(studentTable)\n        val values = ContentValues()\n        values.put(\"name\", \"Zuckerburg\")\n        var affectedRows = LitePal.updateAll<Student>(values)\n        assertEquals(allRows, affectedRows)\n        val table = DBUtility.getIntermediateTableName(studentTable, DBUtility.getTableNameByClassName(Teacher::class.java.name))\n        allRows = getRowsCount(table)\n        values.clear()\n        values.putNull(studentTable!! + \"_id\")\n        affectedRows = LitePal.updateAll(table, values)\n        assertEquals(allRows, affectedRows)\n    }\n\n    @Test\n    fun testUpdateAllWithStaticUpdateButWrongConditions() {\n        val values = ContentValues()\n        values.put(\"name\", \"Dustee\")\n        try {\n            LitePal.updateAll<Student>(values, \"name = 'Dustin'\", \"aaa\")\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\"The parameters in conditions are incorrect.\", e.message)\n        }\n\n        try {\n            LitePal.updateAll<Student>(values, null, null)\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\"The parameters in conditions are incorrect.\", e.message)\n        }\n\n        try {\n            LitePal.updateAll<Student>(values, \"address = ?\", \"HK\")\n            fail()\n        } catch (e: SQLiteException) {\n        }\n\n    }\n\n    @Test\n    fun testUpdateAllWithInstanceUpdate() {\n        var s: Student\n        val ids = IntArray(5)\n        for (i in 0..4) {\n            s = Student()\n            s.name = \"Jessica\"\n            s.age = i + 10\n            s.save()\n            ids[i] = s.id\n        }\n        val date = Date()\n        val toUpdate = Student()\n        toUpdate.age = 24\n        toUpdate.birthday = date\n        var affectedRows = toUpdate.updateAll(\"name = ? and age = ?\", \"Jessica\", \"13\")\n        assertEquals(1, affectedRows)\n        val updatedStu = LitePal.find(Student::class.java, ids[3].toLong())\n        assertEquals(24, updatedStu!!.age)\n        assertEquals(date.time, updatedStu.birthday.time)\n        toUpdate.age = 18\n        toUpdate.name = \"Jess\"\n        affectedRows = toUpdate.updateAll(\"name = ?\", \"Jessica\")\n        assertEquals(5, affectedRows)\n        val students = getStudents(ids)\n        for (updatedStudent in students) {\n            assertEquals(\"Jess\", updatedStudent.name)\n            assertEquals(18, updatedStudent.age)\n        }\n    }\n\n    @Test\n    fun testUpdateAllRowsWithInstanceUpdate() {\n        val c = Connector.getDatabase().query(studentTable, null, null, null, null, null, null)\n        val allRows = c.count\n        c.close()\n        val student = Student()\n        student.name = \"Zuckerburg\"\n        val affectedRows = student.updateAll()\n        assertEquals(allRows, affectedRows)\n    }\n\n    @Test\n    fun testUpdateAllWithDefaultValueWithInstanceUpdate() {\n        var tea: Teacher?\n        val ids = IntArray(5)\n        for (i in 0..4) {\n            tea = Teacher()\n            tea.teacherName = \"Rose Jackson\"\n            tea.age = 50\n            tea.teachYears = 15\n            tea.isSex = false\n            tea.save()\n            ids[i] = tea.id\n        }\n        val t = Teacher()\n        t.teacherName = \"\"\n        t.teachYears = 0\n        t.isSex = true\n        t.age = 22\n        val affectedTeacher = t.updateAll(\"teachername = 'Rose Jackson'\")\n        assertEquals(0, affectedTeacher)\n        val teachers = getTeachers(ids)\n        for (updatedTeacher in teachers) {\n            assertEquals(\"Rose Jackson\", updatedTeacher.teacherName)\n            assertEquals(50, updatedTeacher.age)\n            assertEquals(15, updatedTeacher.teachYears)\n            assertEquals(false, updatedTeacher.isSex)\n        }\n    }\n\n    @Test\n    fun testUpdateAllToDefaultValueWithInstanceUpdate() {\n        var stu: Student\n        val ids = IntArray(5)\n        for (i in 0..4) {\n            stu = Student()\n            stu.name = \"Michael Jackson\"\n            stu.age = 18\n            stu.save()\n            ids[i] = stu.id\n        }\n        val s = Student()\n        s.setToDefault(\"age\")\n        s.setToDefault(\"name\")\n        val affectedStudent = s.updateAll(\"name = 'Michael Jackson'\")\n        assertEquals(5, affectedStudent)\n        val students = getStudents(ids)\n        for (updatedStudent in students) {\n            assertEquals(null, updatedStudent.name)\n            assertEquals(0, updatedStudent.age)\n        }\n    }\n\n    @Test\n    fun testUpdateAllToDefaultValueWithInstanceUpdateButWrongField() {\n        try {\n            val t = Teacher()\n            t.setToDefault(\"name\")\n            t.updateAll(\"\")\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\n                    \"The name field in com.litepaltest.model.Teacher class is necessary which does not exist.\",\n                    e.message)\n        }\n\n    }\n\n    @Test\n    fun testUpdateAllWithInstanceUpdateButWrongConditions() {\n        val student = Student()\n        student.name = \"Dustee\"\n        try {\n            student.updateAll(\"name = 'Dustin'\", \"aaa\")\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\"The parameters in conditions are incorrect.\", e.message)\n        }\n\n        try {\n            student.updateAll(null, null)\n            fail()\n        } catch (e: DataSupportException) {\n            assertEquals(\"The parameters in conditions are incorrect.\", e.message)\n        }\n\n        try {\n            student.updateAll(\"address = ?\", \"HK\")\n            fail()\n        } catch (e: Exception) {\n        }\n\n    }\n\n    @Test\n    fun testUpdateGenericData() {\n        val c = Classroom()\n        c.name = \"Math room\"\n        c.news.add(\"news\")\n        c.news.add(\"paper\")\n        c.update(classroom!!._id.toLong())\n        var result = LitePal.find<Classroom>(classroom!!._id.toLong())\n        assertEquals(\"Math room\", result!!.name)\n        val builder = StringBuilder()\n        for (s in result.news) {\n            builder.append(s)\n        }\n        assertEquals(\"newspaper\", builder.toString())\n        assertEquals(2, result.numbers.size)\n        val c2 = Classroom()\n        c2.setToDefault(\"numbers\")\n        c2.update(classroom!!._id.toLong())\n        result = LitePal.find<Classroom>(classroom!!._id.toLong())\n        assertEquals(\"Math room\", result!!.name)\n        assertEquals(2, result.news.size)\n        assertEquals(0, result.numbers.size)\n    }\n\n\n}"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/crud/update/UpdateUsingUpdateMethodTest.java",
    "content": "package com.litepaltest.test.crud.update;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.LitePal;\nimport org.litepal.exceptions.DataSupportException;\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.util.DBUtility;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteException;\nimport androidx.test.filters.SmallTest;\n\nimport com.litepaltest.model.Classroom;\nimport com.litepaltest.model.Computer;\nimport com.litepaltest.model.Student;\nimport com.litepaltest.model.Teacher;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertNull;\nimport static junit.framework.TestCase.fail;\n\n@SmallTest\npublic class UpdateUsingUpdateMethodTest extends LitePalTestCase {\n\n\tprivate Teacher teacher;\n\n\tprivate Teacher t1;\n\n\tprivate Teacher t2;\n\n\tprivate Student student;\n\n\tprivate Student s1;\n\n\tprivate Student s2;\n\n\tprivate Student s3;\n\n\tprivate Classroom classroom;\n\n\tprivate Classroom c1;\n\n\tprivate Classroom c2;\n\n    private String studentTable;\n\n\t@Before\n\tpublic void setUp() {\n        studentTable = DBUtility.getTableNameByClassName(Student.class.getName());\n\t\tinit();\n\t}\n\n\tprivate void init() {\n\t\tclassroom = new Classroom();\n\t\tclassroom.setName(\"English room\");\n        classroom.getNews().add(\"hello\");\n        classroom.getNews().add(\"world\");\n        classroom.getNumbers().add(123);\n        classroom.getNumbers().add(456);\n\t\tteacher = new Teacher();\n\t\tteacher.setTeacherName(\"Tony\");\n\t\tteacher.setTeachYears(3);\n\t\tteacher.setAge(23);\n\t\tteacher.setSex(false);\n\t\tstudent = new Student();\n\t\tstudent.setName(\"Jonny\");\n\t\tstudent.setAge(13);\n\t\tstudent.setClassroom(classroom);\n\t\tstudent.setBirthday(new Date());\n\t\tstudent.getTeachers().add(teacher);\n\t\tteacher.getStudents().add(student);\n\t\tstudent.save();\n\t\tteacher.save();\n\t\tclassroom.save();\n\t}\n\n\tprivate void initForAssociations() {\n\t\tc1 = new Classroom();\n\t\tc1.setName(\"Working room\");\n\t\tc2 = new Classroom();\n\t\tc2.setName(\"Resting room\");\n\t\ts1 = new Student();\n\t\ts1.setName(\"Parker\");\n\t\ts1.setAge(18);\n\t\ts2 = new Student();\n\t\ts2.setName(\"Peter\");\n\t\ts2.setAge(19);\n\t\ts3 = new Student();\n\t\ts3.setName(\"Miley\");\n\t\ts3.setAge(16);\n\t\tt1 = new Teacher();\n\t\tt1.setTeacherName(\"Jackson\");\n\t\tt1.setTeachYears(3);\n\t\tt1.setAge(28);\n\t\tt2 = new Teacher();\n\t\tt2.setTeacherName(\"Rose\");\n\t\tt2.setTeachYears(12);\n\t\tt2.setAge(34);\n\t}\n\n    @Test\n\tpublic void testUpdateWithStaticUpdate() {\n\t\tContentValues values = new ContentValues();\n\t\tvalues.put(\"TEACHERNAME\", \"Toy\");\n\t\tint rowsAffected = LitePal.update(Teacher.class, values, teacher.getId());\n\t\tassertEquals(1, rowsAffected);\n\t\tassertEquals(\"Toy\", getTeacher(teacher.getId()).getTeacherName());\n\t\tvalues.clear();\n\t\tvalues.put(\"aGe\", 15);\n\t\trowsAffected = LitePal.update(Student.class, values, student.getId());\n\t\tassertEquals(1, rowsAffected);\n\t\tassertEquals(15, getStudent(student.getId()).getAge());\n\t}\n\n    @Test\n\tpublic void testUpdateWithStaticUpdateButWrongClass() {\n\t\tContentValues values = new ContentValues();\n\t\tvalues.put(\"TEACHERNAME\", \"Toy\");\n\t\ttry {\n            LitePal.update(Object.class, values, teacher.getId());\n\t\t} catch (SQLiteException ignored) {\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateWithStaticUpdateButWrongColumn() {\n\t\tContentValues values = new ContentValues();\n\t\tvalues.put(\"TEACHERYEARS\", 13);\n\t\ttry {\n            LitePal.update(Teacher.class, values, teacher.getId());\n\t\t\tfail(\"no such column: TEACHERYEARS\");\n\t\t} catch (SQLiteException ignored) {\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateWithStaticUpdateButNotExistsRecord() {\n\t\tContentValues values = new ContentValues();\n\t\tvalues.put(\"TEACHERNAME\", \"Toy\");\n\t\tint rowsAffected = LitePal.update(Teacher.class, values, 998909);\n\t\tassertEquals(0, rowsAffected);\n\t}\n\n    @Test\n\tpublic void testUpdateWithInstanceUpdate() {\n\t\tTeacher t = new Teacher();\n\t\tt.setAge(66);\n\t\tt.setTeacherName(\"Jobs\");\n\t\tt.setTeachYears(33);\n\t\tt.setSex(false);\n\t\tint rowsAffected = t.update(teacher.getId());\n\t\tassertEquals(1, rowsAffected);\n\t\tTeacher newTeacher = getTeacher(teacher.getId());\n\t\tassertEquals(\"Jobs\", newTeacher.getTeacherName());\n\t\tassertEquals(33, newTeacher.getTeachYears());\n\t\tassertEquals(66, newTeacher.getAge());\n\t}\n\n    @Test\n\tpublic void testUpdateWithDefaultValueWithInstanceUpdate() {\n\t\tTeacher t = new Teacher();\n\t\tt.setTeacherName(\"\");\n\t\tt.setTeachYears(0);\n\t\tt.setSex(true);\n\t\tt.setAge(22);\n\t\tint affectedTeacher = t.update(teacher.getId());\n\t\tassertEquals(0, affectedTeacher);\n\t\tTeacher newTeacher = getTeacher(teacher.getId());\n\t\tassertEquals(teacher.getAge(), newTeacher.getAge());\n\t\tassertEquals(teacher.getTeacherName(), newTeacher.getTeacherName());\n\t\tassertEquals(teacher.getTeachYears(), newTeacher.getTeachYears());\n\t\tassertEquals(teacher.isSex(), newTeacher.isSex());\n\t\tStudent s = new Student();\n\t\ts.setName(null);\n\t\ts.setAge(0);\n\t\tint affectedStudent = s.update(student.getId());\n\t\tassertEquals(0, affectedStudent);\n\t\tStudent newStudent = getStudent(student.getId());\n\t\tassertEquals(student.getName(), newStudent.getName());\n\t\tassertEquals(student.getAge(), newStudent.getAge());\n\t}\n\n    @Test\n\tpublic void testUpdateToDefaultValueWithInstanceUpdate() {\n\t\tStudent s = new Student();\n\t\ts.setToDefault(\"age\");\n\t\ts.setToDefault(\"name\");\n\t\ts.setToDefault(\"birthday\");\n\t\tint affectedStudent = s.update(student.getId());\n\t\tassertEquals(1, affectedStudent);\n\t\tStudent newStudent = LitePal.find(Student.class, student.getId());\n\t\tassertNull(newStudent.getBirthday());\n\t\tassertNull(newStudent.getName());\n\t\tassertEquals(0, newStudent.getAge());\n\t\tTeacher t = new Teacher();\n\t\tt.setAge(45);\n\t\tt.setTeachYears(5);\n\t\tt.setTeacherName(\"John\");\n\t\tt.setToDefault(\"teacherName\");\n\t\tt.setToDefault(\"age\");\n\t\tint affectedTeacher = t.update(teacher.getId());\n\t\tassertEquals(1, affectedTeacher);\n\t\tTeacher newTeacher = getTeacher(teacher.getId());\n\t\tassertEquals(22, newTeacher.getAge());\n\t\tassertEquals(\"\", newTeacher.getTeacherName());\n\t\tassertEquals(5, newTeacher.getTeachYears());\n\t}\n\n    @Test\n\tpublic void testUpdateToDefaultValueWithInstanceUpdateButWrongField() {\n\t\ttry {\n\t\t\tTeacher t = new Teacher();\n\t\t\tt.setToDefault(\"name\");\n\t\t\tt.update(t.getId());\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\n\t\t\t\t\t\"The name field in com.litepaltest.model.Teacher class is necessary which does not exist.\",\n\t\t\t\t\te.getMessage());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateWithInstanceUpdateWithConstructor() {\n\t\ttry {\n\t\t\tComputer computer = new Computer(\"ACER\", 5444);\n\t\t\tcomputer.save();\n\t\t\tcomputer.update(computer.getId());\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\"com.litepaltest.model.Computer needs a default constructor.\",\n\t\t\t\t\te.getMessage());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateWithInstanceUpdateButNotExistsRecord() {\n\t\tTeacher t = new Teacher();\n\t\tt.setTeacherName(\"Johnny\");\n\t\tint rowsAffected = t.update(189876465);\n\t\tassertEquals(0, rowsAffected);\n\t}\n\n\t// public void testUpdateM2OAssociationsOnMSideWithInstanceUpdate() {\n\t// initForAssociations();\n\t// s1.setClassroom(c1);\n\t// s2.setClassroom(c1);\n\t// assertTrue(c1.save());\n\t// assertTrue(c2.save());\n\t// assertTrue(s1.save());\n\t// assertTrue(s2.save());\n\t// Student st = new Student();\n\t// st.setClassroom(c2);\n\t// int rowsAffected = st.update(s1.getId());\n\t// assertEquals(1, rowsAffected);\n\t// rowsAffected = st.update(s2.getId());\n\t// assertEquals(1, rowsAffected);\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s1.getId()));\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s2.getId()));\n\t// }\n\t//\n\t// public void\n\t// testUpdateM2OAssociationsAndOtherFieldsOnMSideWithInstanceUpdate() {\n\t// initForAssociations();\n\t// s1.setClassroom(c1);\n\t// s2.setClassroom(c1);\n\t// assertTrue(c1.save());\n\t// assertTrue(c2.save());\n\t// assertTrue(s1.save());\n\t// assertTrue(s2.save());\n\t// Student st = new Student();\n\t// st.setName(\"Jackson\");\n\t// st.setClassroom(c2);\n\t// int rowsAffected = st.update(s1.getId());\n\t// assertEquals(1, rowsAffected);\n\t// rowsAffected = st.update(s2.getId());\n\t// assertEquals(1, rowsAffected);\n\t// assertEquals(\"Jackson\", getStudent(s1.getId()).getName());\n\t// assertEquals(\"Jackson\", getStudent(s2.getId()).getName());\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s1.getId()));\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s2.getId()));\n\t// }\n\t//\n\t// public void testUpdateM2OAssociationsOnOSideWithInstanceUpdate() {\n\t// initForAssociations();\n\t// c1.getStudentCollection().add(s1);\n\t// c1.getStudentCollection().add(s2);\n\t// assertTrue(c1.save());\n\t// assertTrue(c2.save());\n\t// assertTrue(s1.save());\n\t// assertTrue(s2.save());\n\t// Classroom c = new Classroom();\n\t// c.getStudentCollection().add(s1);\n\t// c.getStudentCollection().add(s2);\n\t// int rowsAffected = c.update(c2.get_id());\n\t// assertEquals(2, rowsAffected);\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s1.getId()));\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s2.getId()));\n\t// }\n\t//\n\t// public void\n\t// testUpdateM2OAssociationsAndOtherFieldsOnOSideWithInstanceUpdate() {\n\t// initForAssociations();\n\t// c1.getStudentCollection().add(s1);\n\t// c1.getStudentCollection().add(s2);\n\t// assertTrue(c1.save());\n\t// assertTrue(c2.save());\n\t// assertTrue(s1.save());\n\t// assertTrue(s2.save());\n\t// Classroom c = new Classroom();\n\t// c.setName(\"Game room\");\n\t// c.getStudentCollection().add(s1);\n\t// c.getStudentCollection().add(s2);\n\t// int rowsAffected = c.update(c2.get_id());\n\t// assertEquals(3, rowsAffected);\n\t// assertEquals(\"Game room\", getClassroom(c2.get_id()).getName());\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s1.getId()));\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s2.getId()));\n\t// }\n\t//\n\t// public void\n\t// testUpdateM2OAssociationsOnMSideWithNotExistsRecordWithInstanceUpdate() {\n\t// initForAssociations();\n\t// s1.setClassroom(c1);\n\t// s2.setClassroom(c1);\n\t// assertTrue(c1.save());\n\t// assertTrue(s1.save());\n\t// assertTrue(s2.save());\n\t// Student s = new Student();\n\t// s.setClassroom(c2);\n\t// int rowsAffected = s.update(s1.getId());\n\t// assertEquals(0, rowsAffected);\n\t// s.update(s2.getId());\n\t// assertEquals(0, rowsAffected);\n\t// assertEquals(c1.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s1.getId()));\n\t// assertEquals(c1.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s2.getId()));\n\t// }\n\t//\n\t// public void\n\t// testUpdateM2OAssociationsOnOSideWithNotExistsRecordWithInstanceUpdate() {\n\t// initForAssociations();\n\t// c1.getStudentCollection().add(s1);\n\t// c1.getStudentCollection().add(s2);\n\t// assertTrue(c1.save());\n\t// assertTrue(c2.save());\n\t// assertTrue(s1.save());\n\t// Classroom c = new Classroom();\n\t// c.getStudentCollection().add(s1);\n\t// c.getStudentCollection().add(s2);\n\t// c.update(c2.get_id());\n\t// assertEquals(c2.get_id(), getForeignKeyValue(\"student\", \"classroom\",\n\t// s1.getId()));\n\t// }\n\t//\n\t// public void testUpdateM2OAssociationsOnMSideWithNullWithInstanceUpdate()\n\t// {\n\t// initForAssociations();\n\t// s1.setClassroom(c1);\n\t// s2.setClassroom(c1);\n\t// assertTrue(c1.save());\n\t// assertTrue(s1.save());\n\t// assertTrue(s2.save());\n\t// }\n\n    @Test\n\tpublic void testUpdateAllWithStaticUpdate() {\n\t\tStudent s;\n\t\tint[] ids = new int[5];\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\ts = new Student();\n\t\t\ts.setName(\"Dusting\");\n\t\t\ts.setAge(i + 10);\n\t\t\ts.save();\n\t\t\tids[i] = s.getId();\n\t\t}\n\t\tContentValues values = new ContentValues();\n\t\tvalues.put(\"age\", 24);\n\t\tint affectedRows = LitePal.updateAll(Student.class, values, \"name = ? and age = ?\",\n\t\t\t\t\"Dusting\", \"13\");\n\t\tassertEquals(1, affectedRows);\n\t\tStudent updatedStu = getStudent(ids[3]);\n\t\tassertEquals(24, updatedStu.getAge());\n\t\tvalues.clear();\n\t\tvalues.put(\"name\", \"Dustee\");\n\t\taffectedRows = LitePal.updateAll(Student.class, values, \"name = ?\", \"Dusting\");\n\t\tassertEquals(5, affectedRows);\n\t\tList<Student> students = getStudents(ids);\n\t\tfor (Student updatedStudent : students) {\n\t\t\tassertEquals(\"Dustee\", updatedStudent.getName());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateAllRowsWithStaticUpdate() {\n\t\tint allRows = getRowsCount(studentTable);\n\t\tContentValues values = new ContentValues();\n\t\tvalues.put(\"name\", \"Zuckerburg\");\n\t\tint affectedRows = LitePal.updateAll(Student.class, values);\n\t\tassertEquals(allRows, affectedRows);\n        String table = DBUtility.getIntermediateTableName(studentTable, DBUtility.getTableNameByClassName(Teacher.class.getName()));\n\t\tallRows = getRowsCount(table);\n\t\tvalues.clear();\n\t\tvalues.putNull(studentTable + \"_id\");\n\t\taffectedRows = LitePal.updateAll(table, values);\n\t\tassertEquals(allRows, affectedRows);\n\t}\n\n    @Test\n\tpublic void testUpdateAllWithStaticUpdateButWrongConditions() {\n\t\tContentValues values = new ContentValues();\n\t\tvalues.put(\"name\", \"Dustee\");\n\t\ttry {\n            LitePal.updateAll(Student.class, values, \"name = 'Dustin'\", \"aaa\");\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\"The parameters in conditions are incorrect.\", e.getMessage());\n\t\t}\n\t\ttry {\n            LitePal.updateAll(Student.class, values, null, null);\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\"The parameters in conditions are incorrect.\", e.getMessage());\n\t\t}\n\t\ttry {\n            LitePal.updateAll(Student.class, values, \"address = ?\", \"HK\");\n\t\t\tfail();\n\t\t} catch (SQLiteException ignored) {\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateAllWithInstanceUpdate() {\n\t\tStudent s;\n\t\tint[] ids = new int[5];\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\ts = new Student();\n\t\t\ts.setName(\"Jessica\");\n\t\t\ts.setAge(i + 10);\n\t\t\ts.save();\n\t\t\tids[i] = s.getId();\n\t\t}\n\t\tDate date = new Date();\n\t\tStudent toUpdate = new Student();\n\t\ttoUpdate.setAge(24);\n\t\ttoUpdate.setBirthday(date);\n\t\tint affectedRows = toUpdate.updateAll(\"name = ? and age = ?\", \"Jessica\", \"13\");\n\t\tassertEquals(1, affectedRows);\n\t\tStudent updatedStu = LitePal.find(Student.class, ids[3]);\n\t\tassertEquals(24, updatedStu.getAge());\n\t\tassertEquals(date.getTime(), updatedStu.getBirthday().getTime());\n\t\ttoUpdate.setAge(18);\n\t\ttoUpdate.setName(\"Jess\");\n\t\taffectedRows = toUpdate.updateAll(\"name = ?\", \"Jessica\");\n\t\tassertEquals(5, affectedRows);\n\t\tList<Student> students = getStudents(ids);\n\t\tfor (Student updatedStudent : students) {\n\t\t\tassertEquals(\"Jess\", updatedStudent.getName());\n\t\t\tassertEquals(18, updatedStudent.getAge());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateAllRowsWithInstanceUpdate() {\n\t\tCursor c = Connector.getDatabase().query(studentTable, null, null, null, null, null, null);\n\t\tint allRows = c.getCount();\n\t\tc.close();\n\t\tStudent student = new Student();\n\t\tstudent.setName(\"Zuckerburg\");\n\t\tint affectedRows = student.updateAll();\n\t\tassertEquals(allRows, affectedRows);\n\t}\n\n    @Test\n\tpublic void testUpdateAllWithDefaultValueWithInstanceUpdate() {\n\t\tTeacher tea;\n\t\tint[] ids = new int[5];\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\ttea = new Teacher();\n\t\t\ttea.setTeacherName(\"Rose Jackson\");\n\t\t\ttea.setAge(50);\n\t\t\ttea.setTeachYears(15);\n\t\t\ttea.setSex(false);\n\t\t\ttea.save();\n\t\t\tids[i] = tea.getId();\n\t\t}\n\t\tTeacher t = new Teacher();\n\t\tt.setTeacherName(\"\");\n\t\tt.setTeachYears(0);\n\t\tt.setSex(true);\n\t\tt.setAge(22);\n\t\tint affectedTeacher = t.updateAll(\"teachername = 'Rose Jackson'\");\n\t\tassertEquals(0, affectedTeacher);\n\t\tList<Teacher> teachers = getTeachers(ids);\n\t\tfor (Teacher updatedTeacher : teachers) {\n\t\t\tassertEquals(\"Rose Jackson\", updatedTeacher.getTeacherName());\n\t\t\tassertEquals(50, updatedTeacher.getAge());\n\t\t\tassertEquals(15, updatedTeacher.getTeachYears());\n\t\t\tassertFalse(updatedTeacher.isSex());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateAllToDefaultValueWithInstanceUpdate() {\n\t\tStudent stu;\n\t\tint[] ids = new int[5];\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tstu = new Student();\n\t\t\tstu.setName(\"Michael Jackson\");\n\t\t\tstu.setAge(18);\n\t\t\tstu.save();\n\t\t\tids[i] = stu.getId();\n\t\t}\n\t\tStudent s = new Student();\n\t\ts.setToDefault(\"age\");\n\t\ts.setToDefault(\"name\");\n\t\tint affectedStudent = s.updateAll(\"name = 'Michael Jackson'\");\n\t\tassertEquals(5, affectedStudent);\n\t\tList<Student> students = getStudents(ids);\n\t\tfor (Student updatedStudent : students) {\n\t\t\tassertNull(updatedStudent.getName());\n\t\t\tassertEquals(0, updatedStudent.getAge());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateAllToDefaultValueWithInstanceUpdateButWrongField() {\n\t\ttry {\n\t\t\tTeacher t = new Teacher();\n\t\t\tt.setToDefault(\"name\");\n\t\t\tt.updateAll(\"\");\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\n\t\t\t\t\t\"The name field in com.litepaltest.model.Teacher class is necessary which does not exist.\",\n\t\t\t\t\te.getMessage());\n\t\t}\n\t}\n\n    @Test\n\tpublic void testUpdateAllWithInstanceUpdateButWrongConditions() {\n\t\tStudent student = new Student();\n\t\tstudent.setName(\"Dustee\");\n\t\ttry {\n\t\t\tstudent.updateAll(\"name = 'Dustin'\", \"aaa\");\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\"The parameters in conditions are incorrect.\", e.getMessage());\n\t\t}\n\t\ttry {\n\t\t\tstudent.updateAll(null, null);\n\t\t\tfail();\n\t\t} catch (DataSupportException e) {\n\t\t\tassertEquals(\"The parameters in conditions are incorrect.\", e.getMessage());\n\t\t}\n\t\ttry {\n\t\t\tstudent.updateAll(\"address = ?\", \"HK\");\n\t\t\tfail();\n\t\t} catch (Exception ignored) {\n\t\t}\n\t}\n\n    @Test\n    public void testUpdateGenericData() {\n        Classroom c = new Classroom();\n        c.setName(\"Math room\");\n        c.getNews().add(\"news\");\n        c.getNews().add(\"paper\");\n        c.update(classroom.get_id());\n        Classroom result = LitePal.find(Classroom.class, classroom.get_id());\n        assertEquals(\"Math room\", result.getName());\n        StringBuilder builder = new StringBuilder();\n        for (String s : result.getNews()) {\n            builder.append(s);\n        }\n        assertEquals(\"newspaper\", builder.toString());\n        assertEquals(2, result.getNumbers().size());\n        Classroom c2 = new Classroom();\n        c2.setToDefault(\"numbers\");\n        c2.update(classroom.get_id());\n        result = LitePal.find(Classroom.class, classroom.get_id());\n        assertEquals(\"Math room\", result.getName());\n        assertEquals(2, result.getNews().size());\n        assertEquals(0, result.getNumbers().size());\n    }\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/util/BaseUtilityTest.java",
    "content": "package com.litepaltest.test.util;\n\nimport androidx.test.filters.SmallTest;\n\nimport org.junit.Test;\nimport org.litepal.util.BaseUtility;\n\nimport com.litepaltest.test.LitePalTestCase;\n\nimport static junit.framework.TestCase.assertEquals;\n\n@SmallTest\npublic class BaseUtilityTest extends LitePalTestCase{\n\n    @Test\n\tpublic void testCount() {\n\t\tString string = \" This is a good one. That is a bad one. \";\n\t\tString markThis = \"This\";\n\t\tString markIs = \"is\";\n\t\tString markA = \"a\";\n\t\tString markGood = \"good\";\n\t\tString markOne = \"one\";\n\t\tString markPoint = \".\";\n\t\tString markSpace = \" \";\n\t\tString markThat = \"That\";\n\t\tString markBad = \"bad\";\n\t\tString markNone = \"none\";\n\t\tString markEmpty = \"\";\n\t\tString markNull = null;\n\t\tassertEquals(1, BaseUtility.count(string, markThis));\n\t\tassertEquals(3, BaseUtility.count(string, markIs));\n\t\tassertEquals(4, BaseUtility.count(string, markA));\n\t\tassertEquals(1, BaseUtility.count(string, markGood));\n\t\tassertEquals(2, BaseUtility.count(string, markOne));\n\t\tassertEquals(2, BaseUtility.count(string, markPoint));\n\t\tassertEquals(11, BaseUtility.count(string, markSpace));\n\t\tassertEquals(1, BaseUtility.count(string, markThat));\n\t\tassertEquals(1, BaseUtility.count(string, markBad));\n\t\tassertEquals(0, BaseUtility.count(string, markNone));\n\t\tassertEquals(0, BaseUtility.count(string, markEmpty));\n\t\tassertEquals(0, BaseUtility.count(string, markNull));\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/androidTest/java/com/litepaltest/test/util/DBUtilityTest.java",
    "content": "package com.litepaltest.test.util;\n\nimport android.database.sqlite.SQLiteDatabase;\nimport androidx.test.filters.SmallTest;\nimport android.util.Pair;\n\nimport com.litepaltest.model.Book;\nimport com.litepaltest.model.Cellphone;\nimport com.litepaltest.test.LitePalTestCase;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.util.DBUtility;\n\nimport java.util.Set;\n\nimport static junit.framework.TestCase.assertEquals;\nimport static junit.framework.TestCase.assertTrue;\n\n@SmallTest\npublic class DBUtilityTest extends LitePalTestCase {\n\n    SQLiteDatabase db;\n\n    @Before\n    public void setUp() {\n        db = Connector.getDatabase();\n    }\n\n    @Test\n    public void testFindIndexedColumns() {\n        Pair<Set<String>, Set<String>> pair = DBUtility.findIndexedColumns(DBUtility.getTableNameByClassName(Cellphone.class.getName()), db);\n        Set<String> indexColumns = pair.first;\n        Set<String> uniqueColumns = pair.second;\n        assertEquals(1, indexColumns.size());\n        assertEquals(1, uniqueColumns.size());\n        assertTrue(indexColumns.contains(\"brand\"));\n        assertTrue(uniqueColumns.contains(\"serial\"));\n        pair = DBUtility.findIndexedColumns(DBUtility.getTableNameByClassName(Book.class.getName()), db);\n        indexColumns = pair.first;\n        uniqueColumns = pair.second;\n        assertEquals(0, indexColumns.size());\n        assertEquals(0, uniqueColumns.size());\n    }\n\n}\n"
  },
  {
    "path": "sample/src/main/AndroidManifest.xml",
    "content": "<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.litepal.litepalsample\">\n\n\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n\n    <application\n        android:name=\"org.litepal.litepalsample.MyApplication\"\n        android:icon=\"@drawable/logo\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\" >\n        <meta-data android:name=\"android.max_aspect\" android:value=\"2.3\" />\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.MainActivity\"\n            android:screenOrientation=\"portrait\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.ManageTablesActivity\"\n            android:label=\"@string/manage_table_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.ModelListActivity\"\n            android:label=\"@string/manage_table_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.TableListActivity\"\n            android:label=\"@string/manage_table_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.ModelStructureActivity\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.TableStructureActivity\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.CRUDActivity\"\n            android:label=\"@string/crud_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.SaveSampleActivity\"\n            android:label=\"@string/save_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.UpdateSampleActivity\"\n            android:label=\"@string/update_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.DeleteSampleActivity\"\n            android:label=\"@string/delete_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.QuerySampleActivity\"\n            android:label=\"@string/query_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.AggregateActivity\"\n            android:label=\"@string/aggregate_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.CountSampleActivity\"\n            android:label=\"@string/count_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.MaxSampleActivity\"\n            android:label=\"@string/max_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.MinSampleActivity\"\n            android:label=\"@string/max_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.AverageSampleActivity\"\n            android:label=\"@string/average_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"org.litepal.litepalsample.activity.SumSampleActivity\"\n            android:label=\"@string/sum_sample\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "sample/src/main/assets/litepal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<litepal>\r\n    <!--\r\n\t\tDefine the database name of your application. \r\n\t\tBy default each database name should be end with .db. \r\n\t\tIf you didn't name your database end with .db, \r\n\t\tLitePal would plus the suffix automatically for you.\r\n\t\tFor example:    \r\n    \t<dbname value=\"demo\" />\r\n    -->\r\n    <dbname value=\"sample\" />\r\n\r\n    <!--\r\n    \tDefine the version of your database. Each time you want \r\n    \tto upgrade your database, the version tag would helps.\r\n    \tModify the models you defined in the mapping tag, and just \r\n    \tmake the version value plus one, the upgrade of database\r\n    \twill be processed automatically without concern.\r\n\t\tFor example:    \r\n    \t<version value=\"1\" />\r\n    -->\r\n    <version value=\"1\" />\r\n\r\n    <!--\r\n    \tDefine your models in the list with mapping tag, LitePal will\r\n    \tcreate tables for each mapping class. The supported fields\r\n    \tdefined in models will be mapped into columns.\r\n    \tFor example:    \r\n    \t<list>\r\n    \t\t<mapping class=\"com.test.model.Reader\" />\r\n    \t\t<mapping class=\"com.test.model.Magazine\" />\r\n    \t</list>\r\n    -->\r\n    <list>\r\n        <mapping class=\"org.litepal.litepalsample.model.Album\" />\r\n        <mapping class=\"org.litepal.litepalsample.model.Song\" />\r\n        <mapping class=\"org.litepal.litepalsample.model.Singer\" />\r\n\r\n\r\n        <!-- <mapping class=\"com.litepaltest.model.Classroom\" />\r\n              <mapping class=\"com.litepaltest.model.Teacher\" />\r\n              <mapping class=\"com.litepaltest.model.IdCard\" />\r\n              <mapping class=\"com.litepaltest.model.Student\" />\r\n              <mapping class=\"com.litepaltest.model.Cellphone\" />\r\n              <mapping class=\"com.litepaltest.model.Computer\" />\r\n              <mapping class=\"com.litepaltest.model.Book\" />\r\n              <mapping class=\"com.litepaltest.model.Product\" />\r\n              <mapping class=\"com.litepaltest.model.Headset\" />\r\n              <mapping class=\"com.litepaltest.model.WeChatMessage\" />\r\n              <mapping class=\"com.litepaltest.model.WeiboMessage\" /> -->\r\n    </list>\r\n\r\n    <!--\r\n    \tDefine the cases of the tables and columns name. Java is a\r\n    \tcase sensitive language, while database is case insensitive.\r\n    \tLitePal will turn all classes names and fields names into lowercase\r\n    \tby default while creating or upgrading database. Developers can change\r\n    \tthis behavior into the styles their like. \"keep\" will keep the\r\n    \tcases of classes and fields. \"upper\" will turn all classes names\r\n    \tand fields names into uppercase. \"lower\" will act as default.\r\n    \tDo not change the value after you run your app for the first time,\r\n    \tor it might cause the exception that column can not be found.\r\n    \tvalue options: keep lower upper\r\n    \tFor example:    \r\n    \t<cases value=\"lower\" />\r\n    -->\r\n\r\n    <!--\r\n        Define where the .db file should be. \"internal\" means the .db file\r\n        will be stored in the database folder of internal storage which no\r\n        one can access. \"external\" means the .db file will be stored in the\r\n        path to the directory on the primary external storage device where\r\n        the application can place persistent files it owns which everyone\r\n        can access. \"internal\" will act as default.\r\n        For example:\r\n        <storage value=\"external\" />\r\n    -->\r\n\r\n    <storage value=\"external\" />\r\n\r\n</litepal>"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/MyApplication.java",
    "content": "package org.litepal.litepalsample;\n\nimport android.app.Application;\n\nimport org.litepal.LitePal;\n\npublic class MyApplication extends Application{\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        LitePal.initialize(this);\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/AggregateActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\n\nimport org.litepal.litepalsample.R;\n\npublic class AggregateActivity extends AppCompatActivity implements OnClickListener {\n\n    public static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, AggregateActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.aggregate_layout);\n        Button mCountSampleBtn = findViewById(R.id.count_sample_btn);\n        Button mMaxSampleBtn = findViewById(R.id.max_sample_btn);\n        Button mMinSampleBtn = findViewById(R.id.min_sample_btn);\n        Button mAverageSampleBtn = findViewById(R.id.average_sample_btn);\n        Button mSumSampleBtn = findViewById(R.id.sum_sample_btn);\n\t\tmCountSampleBtn.setOnClickListener(this);\n\t\tmMaxSampleBtn.setOnClickListener(this);\n\t\tmMinSampleBtn.setOnClickListener(this);\n\t\tmAverageSampleBtn.setOnClickListener(this);\n\t\tmSumSampleBtn.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.count_sample_btn:\n\t\t\tCountSampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.max_sample_btn:\n\t\t\tMaxSampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.min_sample_btn:\n\t\t\tMinSampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.average_sample_btn:\n\t\t\tAverageSampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.sum_sample_btn:\n\t\t\tSumSampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/AverageSampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport org.litepal.LitePal;\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.model.Singer;\n\npublic class AverageSampleActivity extends AppCompatActivity implements OnClickListener {\n\n    private EditText mAgeEdit;\n\n\tprivate TextView mResultText;\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, AverageSampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.average_sample_layout);\n        Button mAvgBtn1 = findViewById(R.id.avg_btn1);\n        Button mAvgBtn2 = findViewById(R.id.avg_btn2);\n\t\tmAgeEdit = findViewById(R.id.age_edit);\n\t\tmResultText = findViewById(R.id.result_text);\n\t\tmAvgBtn1.setOnClickListener(this);\n\t\tmAvgBtn2.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View view) {\n\t\tdouble result = 0;\n\t\tswitch (view.getId()) {\n\t\tcase R.id.avg_btn1:\n\t\t\tresult = LitePal.average(Singer.class, \"age\");\n\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\tbreak;\n\t\tcase R.id.avg_btn2:\n\t\t\ttry {\n\t\t\t\tresult = LitePal.where(\"age > ?\", mAgeEdit.getText().toString()).average(\n\t\t\t\t\t\tSinger.class, \"age\");\n\t\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/CRUDActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\n\nimport org.litepal.litepalsample.R;\n\npublic class CRUDActivity extends AppCompatActivity implements OnClickListener {\n\n    public static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, CRUDActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.crud_layout);\n        Button mSaveSampleBtn = findViewById(R.id.save_sample_btn);\n        Button mUpdateSampleBtn = findViewById(R.id.update_sample_btn);\n        Button mDeleteSampleBtn = findViewById(R.id.delete_sample_btn);\n        Button mQuerySampleBtn = findViewById(R.id.query_sample_btn);\n\t\tmSaveSampleBtn.setOnClickListener(this);\n\t\tmUpdateSampleBtn.setOnClickListener(this);\n\t\tmDeleteSampleBtn.setOnClickListener(this);\n\t\tmQuerySampleBtn.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.save_sample_btn:\n\t\t\tSaveSampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.update_sample_btn:\n\t\t\tUpdateSampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.delete_sample_btn:\n\t\t\tDeleteSampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.query_sample_btn:\n\t\t\tQuerySampleActivity.actionStart(this);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/CountSampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport org.litepal.LitePal;\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.model.Singer;\n\npublic class CountSampleActivity extends AppCompatActivity implements OnClickListener {\n\n    private EditText mAgeEdit;\n\n\tprivate TextView mResultText;\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, CountSampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.count_sample_layout);\n        Button mCountBtn1 = findViewById(R.id.count_btn1);\n        Button mCountBtn2 = findViewById(R.id.count_btn2);\n\t\tmAgeEdit = findViewById(R.id.age_edit);\n\t\tmResultText = findViewById(R.id.result_text);\n\t\tmCountBtn1.setOnClickListener(this);\n\t\tmCountBtn2.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View view) {\n\t\tint result = 0;\n\t\tswitch (view.getId()) {\n\t\tcase R.id.count_btn1:\n\t\t\tresult = LitePal.count(Singer.class);\n\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\tbreak;\n\t\tcase R.id.count_btn2:\n\t\t\ttry {\n\t\t\t\tresult = LitePal.where(\"age > ?\", mAgeEdit.getText().toString()).count(\n\t\t\t\t\t\tSinger.class);\n\t\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/DeleteSampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\nimport android.widget.Toast;\n\nimport org.litepal.LitePal;\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.adapter.DataArrayAdapter;\nimport org.litepal.litepalsample.model.Singer;\nimport org.litepal.tablemanager.Connector;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class DeleteSampleActivity extends AppCompatActivity implements OnClickListener {\n\n\tprivate EditText mSingerIdEdit;\n\n\tprivate EditText mNameToDeleteEdit;\n\n\tprivate EditText mAgeToDeleteEdit;\n\n\tprivate ProgressBar mProgressBar;\n\n    private DataArrayAdapter mAdapter;\n\n\tprivate List<List<String>> mList = new ArrayList<>();\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, DeleteSampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.delete_sample_layout);\n\t\tmProgressBar = findViewById(R.id.progress_bar);\n\t\tmSingerIdEdit = findViewById(R.id.singer_id_edit);\n\t\tmNameToDeleteEdit = findViewById(R.id.name_to_delete);\n\t\tmAgeToDeleteEdit = findViewById(R.id.age_to_delete);\n        Button mDeleteBtn1 = findViewById(R.id.delete_btn1);\n        Button mDeleteBtn2 = findViewById(R.id.delete_btn2);\n        ListView mDataListView = findViewById(R.id.data_list_view);\n\t\tmDeleteBtn1.setOnClickListener(this);\n\t\tmDeleteBtn2.setOnClickListener(this);\n\t\tmAdapter = new DataArrayAdapter(this, 0, mList);\n\t\tmDataListView.setAdapter(mAdapter);\n\t\tpopulateDataFromDB();\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.delete_btn1:\n\t\t\ttry {\n\t\t\t\tint rowsAffected = LitePal.delete(Singer.class,\n\t\t\t\t\t\tLong.parseLong(mSingerIdEdit.getText().toString()));\n\t\t\t\tToast.makeText(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tString.format(getString(R.string.number_of_rows_affected),\n\t\t\t\t\t\t\t\tString.valueOf(rowsAffected)), Toast.LENGTH_SHORT).show();\n\t\t\t\tpopulateDataFromDB();\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tToast.makeText(this, getString(R.string.error_param_is_not_valid),\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase R.id.delete_btn2:\n\t\t\ttry {\n\t\t\t\tint rowsAffected = LitePal.deleteAll(Singer.class, \"name=? and age=?\",\n\t\t\t\t\t\tmNameToDeleteEdit.getText().toString(), mAgeToDeleteEdit.getText()\n\t\t\t\t\t\t\t\t.toString());\n\t\t\t\tToast.makeText(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tString.format(getString(R.string.number_of_rows_affected),\n\t\t\t\t\t\t\t\tString.valueOf(rowsAffected)), Toast.LENGTH_SHORT).show();\n\t\t\t\tpopulateDataFromDB();\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tToast.makeText(this, getString(R.string.error_param_is_not_valid),\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void populateDataFromDB() {\n\t\tmProgressBar.setVisibility(View.VISIBLE);\n\t\tnew Thread(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tmList.clear();\n\t\t\t\tList<String> columnList = new ArrayList<String>();\n\t\t\t\tcolumnList.add(\"id\");\n\t\t\t\tcolumnList.add(\"name\");\n\t\t\t\tcolumnList.add(\"age\");\n\t\t\t\tcolumnList.add(\"ismale\");\n\t\t\t\tmList.add(columnList);\n\t\t\t\tCursor cursor = null;\n\t\t\t\ttry {\n\t\t\t\t\tcursor = Connector.getDatabase().rawQuery(\"select * from singer order by id\",\n\t\t\t\t\t\t\tnull);\n\t\t\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\tlong id = cursor.getLong(cursor.getColumnIndex(\"id\"));\n\t\t\t\t\t\t\tString name = cursor.getString(cursor.getColumnIndex(\"name\"));\n\t\t\t\t\t\t\tint age = cursor.getInt(cursor.getColumnIndex(\"age\"));\n\t\t\t\t\t\t\tint isMale = cursor.getInt(cursor.getColumnIndex(\"ismale\"));\n\t\t\t\t\t\t\tList<String> stringList = new ArrayList<String>();\n\t\t\t\t\t\t\tstringList.add(String.valueOf(id));\n\t\t\t\t\t\t\tstringList.add(name);\n\t\t\t\t\t\t\tstringList.add(String.valueOf(age));\n\t\t\t\t\t\t\tstringList.add(String.valueOf(isMale));\n\t\t\t\t\t\t\tmList.add(stringList);\n\t\t\t\t\t\t} while (cursor.moveToNext());\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t} finally {\n\t\t\t\t\tif (cursor != null) {\n\t\t\t\t\t\tcursor.close();\n\t\t\t\t\t}\n\t\t\t\t\trunOnUiThread(new Runnable() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tmProgressBar.setVisibility(View.GONE);\n\t\t\t\t\t\t\tmAdapter.notifyDataSetChanged();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}).start();\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/MainActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\n\nimport org.litepal.litepalsample.R;\n\npublic class MainActivity extends AppCompatActivity implements OnClickListener {\n\n    private static final String TAG = \"MainActivity\";\n\n    @Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.main_layout);\n        Button mManageTableBtn = findViewById(R.id.manage_table_btn);\n        Button mCrudBtn = findViewById(R.id.crud_btn);\n        Button mAggregateBtn = findViewById(R.id.aggregate_btn);\n\t\tmManageTableBtn.setOnClickListener(this);\n\t\tmCrudBtn.setOnClickListener(this);\n\t\tmAggregateBtn.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.manage_table_btn:\n\t\t\tManageTablesActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.crud_btn:\n\t\t\tCRUDActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.aggregate_btn:\n\t\t\tAggregateActivity.actionStart(this);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/ManageTablesActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\n\nimport org.litepal.litepalsample.R;\n\npublic class ManageTablesActivity extends AppCompatActivity implements OnClickListener {\n\n    public static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, ManageTablesActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.manage_tables_layout);\n        Button mCurrentModelStructureBtn = findViewById(R.id.current_model_structure_btn);\n        Button mOperateDatabaseBtn = findViewById(R.id.operate_database_btn);\n\t\tmCurrentModelStructureBtn.setOnClickListener(this);\n\t\tmOperateDatabaseBtn.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.current_model_structure_btn:\n\t\t\tModelListActivity.actionStart(this);\n\t\t\tbreak;\n\t\tcase R.id.operate_database_btn:\n\t\t\tTableListActivity.actionStart(this);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/MaxSampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport org.litepal.LitePal;\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.model.Singer;\n\npublic class MaxSampleActivity extends AppCompatActivity implements OnClickListener {\n\n    private EditText mAgeEdit;\n\n\tprivate TextView mResultText;\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, MaxSampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.max_sample_layout);\n        Button mMaxBtn1 = findViewById(R.id.max_btn1);\n        Button mMaxBtn2 = findViewById(R.id.max_btn2);\n\t\tmAgeEdit = findViewById(R.id.age_edit);\n\t\tmResultText = findViewById(R.id.result_text);\n\t\tmMaxBtn1.setOnClickListener(this);\n\t\tmMaxBtn2.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View view) {\n\t\tint result = 0;\n\t\tswitch (view.getId()) {\n\t\tcase R.id.max_btn1:\n\t\t\tresult = LitePal.max(Singer.class, \"age\", Integer.TYPE);\n\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\tbreak;\n\t\tcase R.id.max_btn2:\n\t\t\ttry {\n\t\t\t\tresult = LitePal.where(\"age < ?\", mAgeEdit.getText().toString()).max(\n\t\t\t\t\t\tSinger.class, \"age\", Integer.TYPE);\n\t\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/MinSampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport org.litepal.LitePal;\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.model.Singer;\n\npublic class MinSampleActivity extends AppCompatActivity implements OnClickListener {\n\n    private EditText mAgeEdit;\n\n\tprivate TextView mResultText;\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, MinSampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.min_sample_layout);\n        Button mMinBtn1 = findViewById(R.id.min_btn1);\n        Button mMinBtn2 = findViewById(R.id.min_btn2);\n\t\tmAgeEdit = findViewById(R.id.age_edit);\n\t\tmResultText = findViewById(R.id.result_text);\n\t\tmMinBtn1.setOnClickListener(this);\n\t\tmMinBtn2.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View view) {\n\t\tint result = 0;\n\t\tswitch (view.getId()) {\n\t\tcase R.id.min_btn1:\n\t\t\tresult = LitePal.min(Singer.class, \"age\", Integer.TYPE);\n\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\tbreak;\n\t\tcase R.id.min_btn2:\n\t\t\ttry {\n\t\t\t\tresult = LitePal.where(\"age > ?\", mAgeEdit.getText().toString()).min(\n\t\t\t\t\t\tSinger.class, \"age\", Integer.TYPE);\n\t\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/ModelListActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.AssetManager;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.AdapterView.OnItemClickListener;\nimport android.widget.ListView;\n\nimport org.litepal.LitePalApplication;\nimport org.litepal.exceptions.ParseConfigurationFileException;\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.adapter.StringArrayAdapter;\nimport org.litepal.util.Const;\nimport org.xmlpull.v1.XmlPullParser;\nimport org.xmlpull.v1.XmlPullParserException;\nimport org.xmlpull.v1.XmlPullParserFactory;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ModelListActivity extends AppCompatActivity {\n\n    private List<String> mList = new ArrayList<>();\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, ModelListActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.model_list_layout);\n        ListView mModelListView = findViewById(R.id.model_listview);\n\t\tpopulateMappingClasses();\n        StringArrayAdapter mAdapter = new StringArrayAdapter(this, 0, mList);\n        mModelListView.setAdapter(mAdapter);\n        mModelListView.setOnItemClickListener(new OnItemClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onItemClick(AdapterView<?> arg0, View view, int index, long id) {\n\t\t\t\tModelStructureActivity.actionStart(ModelListActivity.this, mList.get(index));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void populateMappingClasses() {\n\t\ttry {\n\t\t\tXmlPullParserFactory factory = XmlPullParserFactory.newInstance();\n\t\t\tXmlPullParser xmlPullParser = factory.newPullParser();\n\t\t\txmlPullParser.setInput(getInputStream(), \"UTF-8\");\n\t\t\tint eventType = xmlPullParser.getEventType();\n\t\t\twhile (eventType != XmlPullParser.END_DOCUMENT) {\n\t\t\t\tString nodeName = xmlPullParser.getName();\n\t\t\t\tswitch (eventType) {\n\t\t\t\tcase XmlPullParser.START_TAG: {\n\t\t\t\t\tif (\"mapping\".equals(nodeName)) {\n\t\t\t\t\t\tString className = xmlPullParser.getAttributeValue(\"\", \"class\");\n\t\t\t\t\t\tmList.add(className);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\teventType = xmlPullParser.next();\n\t\t\t}\n\t\t} catch (XmlPullParserException e) {\n\t\t\tthrow new ParseConfigurationFileException(\n\t\t\t\t\tParseConfigurationFileException.FILE_FORMAT_IS_NOT_CORRECT);\n\t\t} catch (IOException e) {\n\t\t\tthrow new ParseConfigurationFileException(ParseConfigurationFileException.IO_EXCEPTION);\n\t\t}\n\t}\n\n\tprivate InputStream getInputStream() throws IOException {\n\t\tAssetManager assetManager = LitePalApplication.getContext().getAssets();\n\t\tString[] fileNames = assetManager.list(\"\");\n\t\tif (fileNames != null && fileNames.length > 0) {\n\t\t\tfor (String fileName : fileNames) {\n\t\t\t\tif (Const.Config.CONFIGURATION_FILE_NAME.equalsIgnoreCase(fileName)) {\n\t\t\t\t\treturn assetManager.open(fileName, AssetManager.ACCESS_BUFFER);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow new ParseConfigurationFileException(\n\t\t\t\tParseConfigurationFileException.CAN_NOT_FIND_LITEPAL_FILE);\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/ModelStructureActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport org.litepal.litepalsample.R;\nimport org.litepal.util.BaseUtility;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ModelStructureActivity extends AppCompatActivity {\n\n\tpublic static final String CLASS_NAME = \"class_name\";\n\n    private String mClassName;\n\n\tprivate List<Field> mList = new ArrayList<>();\n\n\tpublic static void actionStart(Context context, String className) {\n\t\tIntent intent = new Intent(context, ModelStructureActivity.class);\n\t\tintent.putExtra(CLASS_NAME, className);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsetContentView(R.layout.model_structure_layout);\n\t\tmClassName = getIntent().getStringExtra(CLASS_NAME);\n        ListView mModelStructureListView = findViewById(R.id.model_structure_listview);\n\t\tanalyzeModelStructure();\n        ArrayAdapter<Field> mAdapter = new MyArrayAdapter(this, 0, mList);\n\t\tmModelStructureListView.setAdapter(mAdapter);\n\t}\n\n\tprivate void analyzeModelStructure() {\n\t\tClass<?> dynamicClass = null;\n\t\ttry {\n\t\t\tdynamicClass = Class.forName(mClassName);\n\t\t} catch (ClassNotFoundException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\tField[] fields = dynamicClass.getDeclaredFields();\n\t\tfor (Field field : fields) {\n\t\t\tint modifiers = field.getModifiers();\n\t\t\tif (Modifier.isPrivate(modifiers) && !Modifier.isStatic(modifiers)) {\n\t\t\t\tClass<?> fieldTypeClass = field.getType();\n\t\t\t\tString fieldType = fieldTypeClass.getName();\n\t\t\t\tif (BaseUtility.isFieldTypeSupported(fieldType)) {\n\t\t\t\t\tmList.add(field);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tclass MyArrayAdapter extends ArrayAdapter<Field> {\n\n\t\tpublic MyArrayAdapter(Context context, int textViewResourceId, List<Field> objects) {\n\t\t\tsuper(context, textViewResourceId, objects);\n\t\t}\n\n\t\t@Override\n\t\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\t\tView view;\n\t\t\tField field = getItem(position);\n\t\t\tif (convertView == null) {\n\t\t\t\tview = LayoutInflater.from(getContext()).inflate(R.layout.model_structure_item, null);\n\t\t\t} else {\n\t\t\t\tview = convertView;\n\t\t\t}\n\t\t\tTextView text1 = view.findViewById(R.id.text_1);\n\t\t\ttext1.setText(field.getName());\n\t\t\tTextView text2 = view.findViewById(R.id.text_2);\n\t\t\ttext2.setText(field.getType().getName());\n\t\t\treturn view;\n\t\t}\n\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/QuerySampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport org.litepal.litepalsample.R;\n\npublic class QuerySampleActivity extends AppCompatActivity {\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, QuerySampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.query_sample_layout);\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/SaveSampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\nimport android.widget.Toast;\n\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.adapter.DataArrayAdapter;\nimport org.litepal.litepalsample.model.Singer;\nimport org.litepal.tablemanager.Connector;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class SaveSampleActivity extends AppCompatActivity implements OnClickListener {\n\n\tprivate EditText mSingerNameEdit;\n\n\tprivate EditText mSingerAgeEdit;\n\n\tprivate EditText mSingerGenderEdit;\n\n\tprivate ProgressBar mProgressBar;\n\n    private ListView mDataListView;\n\n\tprivate DataArrayAdapter mAdapter;\n\n\tprivate List<List<String>> mList = new ArrayList<>();\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, SaveSampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.save_sample_layout);\n\t\tmProgressBar = findViewById(R.id.progress_bar);\n\t\tmSingerNameEdit = findViewById(R.id.singer_name_edit);\n\t\tmSingerAgeEdit = findViewById(R.id.singer_age_edit);\n\t\tmSingerGenderEdit = findViewById(R.id.singer_gender_edit);\n        Button mSaveBtn = findViewById(R.id.save_btn);\n\t\tmDataListView = findViewById(R.id.data_list_view);\n\t\tmSaveBtn.setOnClickListener(this);\n\t\tmAdapter = new DataArrayAdapter(this, 0, mList);\n\t\tmDataListView.setAdapter(mAdapter);\n\t\tpopulateDataFromDB();\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.save_btn:\n\t\t\ttry {\n\t\t\t\tSinger singer = new Singer();\n\t\t\t\tsinger.setName(mSingerNameEdit.getText().toString());\n\t\t\t\tsinger.setAge(Integer.parseInt(mSingerAgeEdit.getText().toString()));\n\t\t\t\tsinger.setMale(Boolean.parseBoolean(mSingerGenderEdit.getText().toString()));\n\t\t\t\tsinger.save();\n\t\t\t\trefreshListView(singer.getId(), singer.getName(), singer.getAge(),\n\t\t\t\t\t\tsinger.isMale() ? 1 : 0);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tToast.makeText(this, getString(R.string.error_param_is_not_valid),\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void populateDataFromDB() {\n\t\tmProgressBar.setVisibility(View.VISIBLE);\n\t\tnew Thread(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tmList.clear();\n\t\t\t\tList<String> columnList = new ArrayList<String>();\n\t\t\t\tcolumnList.add(\"id\");\n\t\t\t\tcolumnList.add(\"name\");\n\t\t\t\tcolumnList.add(\"age\");\n\t\t\t\tcolumnList.add(\"ismale\");\n\t\t\t\tmList.add(columnList);\n\t\t\t\tCursor cursor = null;\n\t\t\t\ttry {\n\t\t\t\t\tcursor = Connector.getDatabase().rawQuery(\"select * from singer order by id\",\n\t\t\t\t\t\t\tnull);\n\t\t\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\tlong id = cursor.getLong(cursor.getColumnIndex(\"id\"));\n\t\t\t\t\t\t\tString name = cursor.getString(cursor.getColumnIndex(\"name\"));\n\t\t\t\t\t\t\tint age = cursor.getInt(cursor.getColumnIndex(\"age\"));\n\t\t\t\t\t\t\tint isMale = cursor.getInt(cursor.getColumnIndex(\"ismale\"));\n\t\t\t\t\t\t\tList<String> stringList = new ArrayList<String>();\n\t\t\t\t\t\t\tstringList.add(String.valueOf(id));\n\t\t\t\t\t\t\tstringList.add(name);\n\t\t\t\t\t\t\tstringList.add(String.valueOf(age));\n\t\t\t\t\t\t\tstringList.add(String.valueOf(isMale));\n\t\t\t\t\t\t\tmList.add(stringList);\n\t\t\t\t\t\t} while (cursor.moveToNext());\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t} finally {\n\t\t\t\t\tif (cursor != null) {\n\t\t\t\t\t\tcursor.close();\n\t\t\t\t\t}\n\t\t\t\t\trunOnUiThread(new Runnable() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tmProgressBar.setVisibility(View.GONE);\n\t\t\t\t\t\t\tmAdapter.notifyDataSetChanged();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tprivate void refreshListView(long id, String name, int age, int isMale) {\n\t\tList<String> stringList = new ArrayList<String>();\n\t\tstringList.add(String.valueOf(id));\n\t\tstringList.add(name);\n\t\tstringList.add(String.valueOf(age));\n\t\tstringList.add(String.valueOf(isMale));\n\t\tmList.add(stringList);\n\t\tmAdapter.notifyDataSetChanged();\n\t\tmDataListView.setSelection(mList.size());\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/SumSampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport org.litepal.LitePal;\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.model.Singer;\n\npublic class SumSampleActivity extends AppCompatActivity implements OnClickListener {\n\n    private EditText mAgeEdit;\n\n\tprivate TextView mResultText;\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, SumSampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.sum_sample_layout);\n        Button mSumBtn1 = findViewById(R.id.sum_btn1);\n        Button mSumBtn2 = findViewById(R.id.sum_btn2);\n\t\tmAgeEdit = findViewById(R.id.age_edit);\n\t\tmResultText = findViewById(R.id.result_text);\n\t\tmSumBtn1.setOnClickListener(this);\n\t\tmSumBtn2.setOnClickListener(this);\n\t}\n\n\t@Override\n\tpublic void onClick(View view) {\n\t\tint result = 0;\n\t\tswitch (view.getId()) {\n\t\tcase R.id.sum_btn1:\n\t\t\tresult = LitePal.sum(Singer.class, \"age\", Integer.TYPE);\n\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\tbreak;\n\t\tcase R.id.sum_btn2:\n\t\t\ttry {\n\t\t\t\tresult = LitePal.where(\"age > ?\", mAgeEdit.getText().toString()).sum(\n\t\t\t\t\t\tSinger.class, \"age\", Integer.TYPE);\n\t\t\t\tmResultText.setText(String.valueOf(result));\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/TableListActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.AdapterView.OnItemClickListener;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\n\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.adapter.StringArrayAdapter;\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.util.DBUtility;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TableListActivity extends AppCompatActivity {\n\n\tprivate ProgressBar mProgressBar;\n\n    private StringArrayAdapter mAdapter;\n\n\tprivate List<String> mList = new ArrayList<>();\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, TableListActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.table_list_layout);\n\t\tmProgressBar = findViewById(R.id.progress_bar);\n        ListView mTableListView = findViewById(R.id.table_listview);\n\t\tmAdapter = new StringArrayAdapter(this, 0, mList);\n\t\tmTableListView.setAdapter(mAdapter);\n\t\tpopulateTables();\n\t\tmTableListView.setOnItemClickListener(new OnItemClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onItemClick(AdapterView<?> arg0, View view, int index, long id) {\n\t\t\t\tTableStructureActivity.actionStart(TableListActivity.this, mList.get(index));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void populateTables() {\n\t\tmProgressBar.setVisibility(View.VISIBLE);\n\t\tnew Thread(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tList<String> tables = DBUtility.findAllTableNames(Connector.getDatabase());\n\t\t\t\tfor (String table : tables) {\n\t\t\t\t\tif (table.equalsIgnoreCase(\"android_metadata\")\n\t\t\t\t\t\t\t|| table.equalsIgnoreCase(\"sqlite_sequence\")\n\t\t\t\t\t\t\t|| table.equalsIgnoreCase(\"table_schema\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tmList.add(table);\n\t\t\t\t}\n\t\t\t\trunOnUiThread(new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tmProgressBar.setVisibility(View.GONE);\n\t\t\t\t\t\tmAdapter.notifyDataSetChanged();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}).start();\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/TableStructureActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport org.litepal.litepalsample.R;\nimport org.litepal.tablemanager.Connector;\nimport org.litepal.tablemanager.model.ColumnModel;\nimport org.litepal.tablemanager.model.TableModel;\nimport org.litepal.util.DBUtility;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\npublic class TableStructureActivity extends AppCompatActivity {\n\n\tpublic static final String TABLE_NAME = \"table_name\";\n\n    private String mTableName;\n\n\tprivate List<ColumnModel> mList = new ArrayList<>();\n\n\tpublic static void actionStart(Context context, String tableName) {\n\t\tIntent intent = new Intent(context, TableStructureActivity.class);\n\t\tintent.putExtra(TABLE_NAME, tableName);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsetContentView(R.layout.table_structure_layout);\n\t\tmTableName = getIntent().getStringExtra(TABLE_NAME);\n        ListView mTableStructureListView = findViewById(R.id.table_structure_listview);\n\t\tanalyzeTableStructure();\n        ArrayAdapter<ColumnModel> mAdapter = new MyArrayAdapter(this, 0, mList);\n\t\tmTableStructureListView.setAdapter(mAdapter);\n\t}\n\n\tprivate void analyzeTableStructure() {\n\t\tTableModel tableMode = DBUtility.findPragmaTableInfo(mTableName, Connector.getDatabase());\n\t\tCollection<ColumnModel> columnModelList = tableMode.getColumnModels();\n        mList.addAll(columnModelList);\n\t}\n\n\tclass MyArrayAdapter extends ArrayAdapter<ColumnModel> {\n\n\t\tpublic MyArrayAdapter(Context context, int textViewResourceId, List<ColumnModel> objects) {\n\t\t\tsuper(context, textViewResourceId, objects);\n\t\t}\n\n\t\t@Override\n\t\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\t\tView view;\n            ColumnModel columnModel = getItem(position);\n\t\t\tString columnName = columnModel.getColumnName();\n\t\t\tString columnType = columnModel.getColumnType();\n            boolean nullable = columnModel.isNullable();\n            boolean unique = columnModel.isUnique();\n            boolean hasIndex = columnModel.hasIndex();\n            String defaultValue = columnModel.getDefaultValue();\n\t\t\tif (convertView == null) {\n\t\t\t\tview = LayoutInflater.from(getContext()).inflate(R.layout.table_structure_item, null);\n\t\t\t} else {\n\t\t\t\tview = convertView;\n\t\t\t}\n\t\t\tTextView text1 = view.findViewById(R.id.text_1);\n\t\t\ttext1.setText(columnName);\n\t\t\tTextView text2 = view.findViewById(R.id.text_2);\n\t\t\ttext2.setText(columnType);\n            TextView text3 = view.findViewById(R.id.text_3);\n            text3.setText(String.valueOf(nullable));\n            TextView text4 = view.findViewById(R.id.text_4);\n            text4.setText(String.valueOf(unique));\n            TextView text5 = view.findViewById(R.id.text_5);\n            text5.setText(defaultValue);\n\t\t\tTextView text6 = view.findViewById(R.id.text_6);\n\t\t\ttext6.setText(String.valueOf(hasIndex));\n\t\t\treturn view;\n\t\t}\n\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/activity/UpdateSampleActivity.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.activity;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\nimport android.widget.Toast;\n\nimport org.litepal.litepalsample.R;\nimport org.litepal.litepalsample.adapter.DataArrayAdapter;\nimport org.litepal.litepalsample.model.Singer;\nimport org.litepal.tablemanager.Connector;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class UpdateSampleActivity extends AppCompatActivity implements OnClickListener {\n\n\tprivate EditText mSingerIdEdit;\n\n\tprivate EditText mSingerNameEdit;\n\n\tprivate EditText mSingerAgeEdit;\n\n\tprivate EditText mNameToUpdateEdit;\n\n\tprivate EditText mAgeToUpdateEdit;\n\n\tprivate ProgressBar mProgressBar;\n\n    private DataArrayAdapter mAdapter;\n\n\tprivate List<List<String>> mList = new ArrayList<>();\n\n\tpublic static void actionStart(Context context) {\n\t\tIntent intent = new Intent(context, UpdateSampleActivity.class);\n\t\tcontext.startActivity(intent);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.update_sample_layout);\n\t\tmProgressBar = findViewById(R.id.progress_bar);\n\t\tmSingerIdEdit =  findViewById(R.id.singer_id_edit);\n\t\tmSingerNameEdit = findViewById(R.id.singer_name_edit);\n\t\tmSingerAgeEdit = findViewById(R.id.singer_age_edit);\n\t\tmNameToUpdateEdit = findViewById(R.id.name_to_update);\n\t\tmAgeToUpdateEdit = findViewById(R.id.age_to_update);\n        Button mUpdateBtn1 = findViewById(R.id.update_btn1);\n        Button mUpdateBtn2 = findViewById(R.id.update_btn2);\n        ListView mDataListView = findViewById(R.id.data_list_view);\n\t\tmUpdateBtn1.setOnClickListener(this);\n\t\tmUpdateBtn2.setOnClickListener(this);\n\t\tmAdapter = new DataArrayAdapter(this, 0, mList);\n\t\tmDataListView.setAdapter(mAdapter);\n\t\tpopulateDataFromDB();\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.update_btn1:\n\t\t\ttry {\n\t\t\t\tSinger singer = new Singer();\n\t\t\t\tsinger.setName(mSingerNameEdit.getText().toString());\n\t\t\t\tsinger.setAge(Integer.parseInt(mSingerAgeEdit.getText().toString()));\n\t\t\t\tint rowsAffected = singer\n\t\t\t\t\t\t.update(Long.parseLong(mSingerIdEdit.getText().toString()));\n\t\t\t\tToast.makeText(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tString.format(getString(R.string.number_of_rows_affected),\n\t\t\t\t\t\t\t\tString.valueOf(rowsAffected)), Toast.LENGTH_SHORT).show();\n\t\t\t\tpopulateDataFromDB();\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tToast.makeText(this, getString(R.string.error_param_is_not_valid),\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase R.id.update_btn2:\n\t\t\ttry {\n\t\t\t\tSinger singer = new Singer();\n\t\t\t\tsinger.setName(mSingerNameEdit.getText().toString());\n\t\t\t\tsinger.setAge(Integer.parseInt(mSingerAgeEdit.getText().toString()));\n\t\t\t\tint rowsAffected = singer.updateAll(\"name=? and age=?\", mNameToUpdateEdit.getText()\n\t\t\t\t\t\t.toString(), mAgeToUpdateEdit.getText().toString());\n\t\t\t\tToast.makeText(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tString.format(getString(R.string.number_of_rows_affected),\n\t\t\t\t\t\t\t\tString.valueOf(rowsAffected)), Toast.LENGTH_SHORT).show();\n\t\t\t\tpopulateDataFromDB();\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tToast.makeText(this, getString(R.string.error_param_is_not_valid),\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void populateDataFromDB() {\n\t\tmProgressBar.setVisibility(View.VISIBLE);\n\t\tnew Thread(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tmList.clear();\n\t\t\t\tList<String> columnList = new ArrayList<>();\n\t\t\t\tcolumnList.add(\"id\");\n\t\t\t\tcolumnList.add(\"name\");\n\t\t\t\tcolumnList.add(\"age\");\n\t\t\t\tcolumnList.add(\"ismale\");\n\t\t\t\tmList.add(columnList);\n\t\t\t\tCursor cursor = null;\n\t\t\t\ttry {\n\t\t\t\t\tcursor = Connector.getDatabase().rawQuery(\"select * from singer order by id\",\n\t\t\t\t\t\t\tnull);\n\t\t\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\tlong id = cursor.getLong(cursor.getColumnIndex(\"id\"));\n\t\t\t\t\t\t\tString name = cursor.getString(cursor.getColumnIndex(\"name\"));\n\t\t\t\t\t\t\tint age = cursor.getInt(cursor.getColumnIndex(\"age\"));\n\t\t\t\t\t\t\tint isMale = cursor.getInt(cursor.getColumnIndex(\"ismale\"));\n\t\t\t\t\t\t\tList<String> stringList = new ArrayList<>();\n\t\t\t\t\t\t\tstringList.add(String.valueOf(id));\n\t\t\t\t\t\t\tstringList.add(name);\n\t\t\t\t\t\t\tstringList.add(String.valueOf(age));\n\t\t\t\t\t\t\tstringList.add(String.valueOf(isMale));\n\t\t\t\t\t\t\tmList.add(stringList);\n\t\t\t\t\t\t} while (cursor.moveToNext());\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t} finally {\n\t\t\t\t\tif (cursor != null) {\n\t\t\t\t\t\tcursor.close();\n\t\t\t\t\t}\n\t\t\t\t\trunOnUiThread(new Runnable() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tmProgressBar.setVisibility(View.GONE);\n\t\t\t\t\t\t\tmAdapter.notifyDataSetChanged();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}).start();\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/adapter/DataArrayAdapter.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.adapter;\n\nimport java.util.List;\n\nimport org.litepal.litepalsample.util.Utility;\n\nimport android.content.Context;\nimport android.text.TextUtils.TruncateAt;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\npublic class DataArrayAdapter extends ArrayAdapter<List<String>> {\n\n\tpublic DataArrayAdapter(Context context, int textViewResourceId, List<List<String>> objects) {\n\t\tsuper(context, textViewResourceId, objects);\n\t}\n\n\t@Override\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tList<String> dataList = getItem(position);\n\t\tLinearLayout layout;\n\t\tif (convertView == null) {\n\t\t\tlayout = new LinearLayout(getContext());\n\t\t} else {\n\t\t\tlayout = (LinearLayout) convertView;\n\t\t}\n\t\tlayout.removeAllViews();\n\t\tint width = Utility.dp2px(getContext(), 100);\n\t\tint height = Utility.dp2px(getContext(), 30);\n\t\tfor (String data : dataList) {\n\t\t\tLinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, height);\n\t\t\tTextView textView = new TextView(getContext());\n\t\t\ttextView.setText(data);\n\t\t\ttextView.setSingleLine(true);\n\t\t\ttextView.setEllipsize(TruncateAt.END);\n\t\t\ttextView.setGravity(Gravity.CENTER_VERTICAL);\n\t\t\tlayout.addView(textView, params);\n\t\t}\n\t\treturn layout;\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/adapter/StringArrayAdapter.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.adapter;\n\nimport java.util.List;\n\nimport org.litepal.litepalsample.R;\n\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ArrayAdapter;\nimport android.widget.TextView;\n\npublic class StringArrayAdapter extends ArrayAdapter<String> {\n\n\tpublic StringArrayAdapter(Context context, int textViewResourceId, List<String> objects) {\n\t\tsuper(context, textViewResourceId, objects);\n\t}\n\n\t@Override\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tView view;\n\t\tif (convertView == null) {\n\t\t\tview = LayoutInflater.from(getContext()).inflate(R.layout.simple_list_item, null);\n\t\t} else {\n\t\t\tview = convertView;\n\t\t}\n\t\tTextView textView = (TextView) view.findViewById(R.id.text_1);\n\t\ttextView.setText(getItem(position));\n\t\treturn view;\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/model/Album.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.model;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.litepal.crud.LitePalSupport;\n\npublic class Album extends LitePalSupport {\n\n\tprivate long id;\n\n//    @Column(ignore = false, unique = false, nullable = false, defaultValue = \"888\")\n    private int sales;\n\n//    @Column(nullable = false)\n\tprivate String name;\n\n//    @Column(ignore = false, nullable = false)\n\tprivate String publisher;\n\n//    @Column(nullable = false, ignore = false)\n\tprivate double price;\n\n//    @Column(unique = true, ignore = false)\n    private String serial;\n\n//    @Column(ignore = false, nullable = false, defaultValue = \"100\")\n\tprivate Date release;\n\n\tprivate Singer singer;\n\n\tprivate List<Song> songs = new ArrayList<Song>();\n\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getPublisher() {\n\t\treturn publisher;\n\t}\n\n\tpublic void setPublisher(String publisher) {\n\t\tthis.publisher = publisher;\n\t}\n\n\tpublic double getPrice() {\n\t\treturn price;\n\t}\n\n\tpublic void setPrice(double price) {\n\t\tthis.price = price;\n\t}\n\n\tpublic Date getRelease() {\n\t\treturn release;\n\t}\n\n\tpublic void setRelease(Date release) {\n\t\tthis.release = release;\n\t}\n\n\tpublic Singer getSinger() {\n\t\treturn singer;\n\t}\n\n\tpublic void setSinger(Singer singer) {\n\t\tthis.singer = singer;\n\t}\n\n\tpublic List<Song> getSongs() {\n\t\treturn songs;\n\t}\n\n\tpublic void setSongs(List<Song> songs) {\n\t\tthis.songs = songs;\n\t}\n\n    public String getSerial() {\n        return serial;\n    }\n\n    public void setSerial(String serial) {\n        this.serial = serial;\n    }\n\n    public int getSales() {\n        return sales;\n    }\n\n    public void setSales(int sales) {\n        this.sales = sales;\n    }\n\n}\n"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/model/Singer.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.litepal.crud.LitePalSupport;\n\npublic class Singer extends LitePalSupport {\n\n\tprivate long id;\n\n\tprivate String name;\n\n\tprivate int age;\n\n\tprivate boolean isMale;\n\n\tprivate List<Album> albums = new ArrayList<Album>();\n\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic int getAge() {\n\t\treturn age;\n\t}\n\n\tpublic void setAge(int age) {\n\t\tthis.age = age;\n\t}\n\n\tpublic boolean isMale() {\n\t\treturn isMale;\n\t}\n\n\tpublic void setMale(boolean isMale) {\n\t\tthis.isMale = isMale;\n\t}\n\n\tpublic List<Album> getAlbums() {\n\t\treturn albums;\n\t}\n\n\tpublic void setAlbums(List<Album> albums) {\n\t\tthis.albums = albums;\n\t}\n\n}"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/model/Song.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.model;\n\nimport org.litepal.annotation.Column;\nimport org.litepal.crud.LitePalSupport;\n\npublic class Song extends LitePalSupport {\n\n\tprivate long id;\n\n\t@Column(index = true)\n\tprivate String name;\n\n\t@Column(unique = true, index = true)\n\tprivate String lyric;\n\n\tprivate String duration;\n\n\tprivate Album album;\n\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getLyric() {\n\t\treturn lyric;\n\t}\n\n\tpublic void setLyric(String lyric) {\n\t\tthis.lyric = lyric;\n\t}\n\n\tpublic String getDuration() {\n\t\treturn duration;\n\t}\n\n\tpublic void setDuration(String duration) {\n\t\tthis.duration = duration;\n\t}\n\n\tpublic Album getAlbum() {\n\t\treturn album;\n\t}\n\n\tpublic void setAlbum(Album album) {\n\t\tthis.album = album;\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/main/java/org/litepal/litepalsample/util/Utility.java",
    "content": "/*\n * Copyright (C)  Tony Green, Litepal Framework Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.litepal.litepalsample.util;\n\nimport android.content.Context;\n\npublic class Utility {\n\n\tpublic static int dp2px(Context context, float dpValue) {\n\t\tfinal float scale = context.getResources().getDisplayMetrics().density;\n\t\treturn (int) (dpValue * scale + 0.5f);\n\t}\n\n\tpublic static int px2dp(Context context, float pxValue) {\n\t\tfinal float scale = context.getResources().getDisplayMetrics().density;\n\t\treturn (int) (pxValue / scale + 0.5f);\n\t}\n\t\n}"
  },
  {
    "path": "sample/src/main/res/layout/aggregate_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\"\n    android:padding=\"10dp\" >\n\n    <Button\n        android:id=\"@+id/count_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/count_sample\" />\n\n    <Button\n        android:id=\"@+id/max_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/max_sample\" />\n\n    <Button\n        android:id=\"@+id/min_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/min_sample\" />\n\n    <Button\n        android:id=\"@+id/average_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/average_sample\" />\n\n    <Button\n        android:id=\"@+id/sum_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/sum_sample\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/average_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:orientation=\"vertical\"\n    android:padding=\"10dp\" >\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/sample_codes\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_avg_first_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/avg_btn1\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:singleLine=\"true\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_avg_second_line_pre\"\n                    android:textSize=\"18sp\" />\n\n                <EditText\n                    android:id=\"@+id/age_edit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:background=\"@null\"\n                    android:inputType=\"number\"\n                    android:singleLine=\"true\"\n                    android:text=\"@string/twenty_three\"\n                    android:textSize=\"18sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_avg_second_line_end\"\n                    android:textSize=\"18sp\" />\n            </LinearLayout>\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_avg_third_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/avg_btn2\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/result\" />\n\n    <TextView\n        android:id=\"@+id/result_text\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/textfield_default\"\n        android:gravity=\"center_vertical\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/count_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:orientation=\"vertical\"\n    android:padding=\"10dp\" >\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/sample_codes\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_count_first_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/count_btn1\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:singleLine=\"true\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_count_second_line_pre\"\n                    android:textSize=\"18sp\" />\n\n                <EditText\n                    android:id=\"@+id/age_edit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:background=\"@null\"\n                    android:inputType=\"number\"\n                    android:singleLine=\"true\"\n                    android:text=\"@string/twenty_three\"\n                    android:textSize=\"18sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_count_second_line_end\"\n                    android:textSize=\"18sp\" />\n            </LinearLayout>\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_count_third_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/count_btn2\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/result\" />\n\n    <TextView\n        android:id=\"@+id/result_text\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/textfield_default\"\n        android:gravity=\"center_vertical\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/crud_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\"\n    android:padding=\"10dp\" >\n\n    <Button\n        android:id=\"@+id/save_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/save_sample\" />\n\n    <Button\n        android:id=\"@+id/update_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/update_sample\" />\n\n    <Button\n        android:id=\"@+id/delete_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/delete_sample\" />\n\n    <Button\n        android:id=\"@+id/query_sample_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/query_sample\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/delete_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:padding=\"10dp\" >\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/sample_codes\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_delete_first_line_pre\"\n                    android:textSize=\"18sp\" />\n\n                <EditText\n                    android:id=\"@+id/singer_id_edit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:background=\"@null\"\n                    android:inputType=\"number\"\n                    android:singleLine=\"true\"\n                    android:text=\"@string/one\"\n                    android:textSize=\"18sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_delete_first_line_end\"\n                    android:textSize=\"18sp\" />\n            </LinearLayout>\n        </HorizontalScrollView>\n\n        <Button\n            android:id=\"@+id/delete_btn1\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/run_codes_above\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_delete_second_line\"\n                    android:textSize=\"18sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_delete_third_line\"\n                    android:textSize=\"18sp\" />\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" >\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_delete_fourth_line_pre\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/name_to_delete\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"none\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/taylor_swift\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_delete_fourth_line_mid\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/age_to_delete\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"number\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/twenty_five\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_delete_fourth_line_end\"\n                        android:textSize=\"18sp\" />\n                </LinearLayout>\n            </LinearLayout>\n        </HorizontalScrollView>\n\n        <Button\n            android:id=\"@+id/delete_btn2\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/run_codes_above\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"fill_parent\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <ListView\n                android:id=\"@+id/data_list_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:fadingEdge=\"none\"\n                android:overScrollMode=\"never\"\n                android:scrollbars=\"none\" >\n            </ListView>\n        </HorizontalScrollView>\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/progress_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\" />\n\n</RelativeLayout>"
  },
  {
    "path": "sample/src/main/res/layout/main_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:padding=\"10dp\"\n    android:orientation=\"vertical\" >\n\n    <Button\n        android:id=\"@+id/manage_table_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/manage_table\" />\n\n    <Button\n        android:id=\"@+id/crud_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/crud\" />\n\n    <Button\n        android:id=\"@+id/aggregate_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/aggregate_functions\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/manage_tables_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:padding=\"10dp\"\n    android:orientation=\"vertical\" >\n\n    <TextView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/table_generation_tip\" />\n    \n    <Button \n        android:id=\"@+id/current_model_structure_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/current_model_structure\"\n        />\n    \n     <Button \n        android:id=\"@+id/operate_database_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/current_table_structure\"\n        />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/max_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:orientation=\"vertical\"\n    android:padding=\"10dp\" >\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/sample_codes\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_max_first_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/max_btn1\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:singleLine=\"true\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_max_second_line_pre\"\n                    android:textSize=\"18sp\" />\n\n                <EditText\n                    android:id=\"@+id/age_edit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:background=\"@null\"\n                    android:inputType=\"number\"\n                    android:singleLine=\"true\"\n                    android:text=\"@string/twenty_three\"\n                    android:textSize=\"18sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_max_second_line_end\"\n                    android:textSize=\"18sp\" />\n            </LinearLayout>\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_max_third_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/max_btn2\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/result\" />\n\n    <TextView\n        android:id=\"@+id/result_text\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/textfield_default\"\n        android:gravity=\"center_vertical\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/min_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:orientation=\"vertical\"\n    android:padding=\"10dp\" >\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/sample_codes\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_min_first_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/min_btn1\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:singleLine=\"true\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_min_second_line_pre\"\n                    android:textSize=\"18sp\" />\n\n                <EditText\n                    android:id=\"@+id/age_edit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:background=\"@null\"\n                    android:inputType=\"number\"\n                    android:singleLine=\"true\"\n                    android:text=\"@string/twenty_three\"\n                    android:textSize=\"18sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_min_second_line_end\"\n                    android:textSize=\"18sp\" />\n            </LinearLayout>\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_min_third_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/min_btn2\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/result\" />\n\n    <TextView\n        android:id=\"@+id/result_text\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/textfield_default\"\n        android:gravity=\"center_vertical\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/model_list_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\" >\n\t\n    <ListView \n        android:id=\"@+id/model_listview\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        ></ListView>\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/model_structure_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"horizontal\" >\n\n    <TextView\n        android:id=\"@+id/text_1\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"50dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n    <TextView\n        android:id=\"@+id/text_2\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"50dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/model_structure_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\" >\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"#8e8d8e\"\n        android:orientation=\"horizontal\" >\n\n        <TextView\n            android:id=\"@+id/text_1\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"25dp\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"6dip\"\n            android:text=\"@string/field_name\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/text_2\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"25dp\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"6dip\"\n            android:text=\"@string/field_type\"\n            android:textColor=\"#fff\" />\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/model_structure_listview\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" >\n    </ListView>\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/query_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:padding=\"10dp\" >\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/sample_desc_query_first_line\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_query_first_line\"\n                android:textSize=\"18sp\" />\n        </HorizontalScrollView>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"15dp\"\n            android:text=\"@string/sample_desc_query_second_line\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_query_second_line\"\n                android:textSize=\"18sp\" />\n        </HorizontalScrollView>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"15dp\"\n            android:text=\"@string/sample_desc_query_third_line\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_query_third_line\"\n                android:textSize=\"18sp\" />\n        </HorizontalScrollView>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"15dp\"\n            android:text=\"@string/sample_desc_query_fourth_line\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_query_fourth_line\"\n                android:textSize=\"18sp\" />\n        </HorizontalScrollView>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"15dp\"\n            android:text=\"@string/sample_desc_query_fifth_line\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_query_fifth_line\"\n                android:textSize=\"18sp\" />\n        </HorizontalScrollView>\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "sample/src/main/res/layout/save_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:padding=\"10dp\" >\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/sample_codes\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_first_line\"\n                    android:textSize=\"18sp\" />\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:singleLine=\"true\" >\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_second_line_pre\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/singer_name_edit\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"none\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/taylor_swift\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_second_line_end\"\n                        android:textSize=\"18sp\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" >\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_third_line_pre\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/singer_age_edit\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"number\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/twenty_five\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_third_line_end\"\n                        android:textSize=\"18sp\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" >\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_fourth_line_pre\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/singer_gender_edit\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"none\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/boolean_false\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_fourth_line_end\"\n                        android:textSize=\"18sp\" />\n                </LinearLayout>\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_save\"\n                    android:textSize=\"18sp\" />\n            </LinearLayout>\n        </HorizontalScrollView>\n\n        <Button\n            android:id=\"@+id/save_btn\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/run_codes_above\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"fill_parent\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <ListView\n                android:id=\"@+id/data_list_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:fadingEdge=\"none\"\n                android:overScrollMode=\"never\"\n                android:scrollbars=\"none\" >\n            </ListView>\n        </HorizontalScrollView>\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/progress_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\" />\n\n</RelativeLayout>"
  },
  {
    "path": "sample/src/main/res/layout/simple_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\" >\n\n    <TextView\n        android:id=\"@+id/text_1\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"50dp\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/sum_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:orientation=\"vertical\"\n    android:padding=\"10dp\" >\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/sample_codes\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_sum_first_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/sum_btn1\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <HorizontalScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/textfield_default\"\n        android:fadingEdge=\"none\"\n        android:overScrollMode=\"never\"\n        android:scrollbars=\"none\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:singleLine=\"true\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_sum_second_line_pre\"\n                    android:textSize=\"18sp\" />\n\n                <EditText\n                    android:id=\"@+id/age_edit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:background=\"@null\"\n                    android:inputType=\"number\"\n                    android:singleLine=\"true\"\n                    android:text=\"@string/twenty_three\"\n                    android:textSize=\"18sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_sum_second_line_end\"\n                    android:textSize=\"18sp\" />\n            </LinearLayout>\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sample_codes_sum_third_line\"\n                android:textSize=\"18sp\" />\n        </LinearLayout>\n    </HorizontalScrollView>\n\n    <Button\n        android:id=\"@+id/sum_btn2\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/run_codes_above\" />\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/result\" />\n\n    <TextView\n        android:id=\"@+id/result_text\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"40dp\"\n        android:background=\"@drawable/textfield_default\"\n        android:gravity=\"center_vertical\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/table_list_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\" >\n\n    <ListView\n        android:id=\"@+id/table_listview\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" >\n    </ListView>\n    \n    <ProgressBar \n        android:id=\"@+id/progress_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        />\n\n</RelativeLayout>"
  },
  {
    "path": "sample/src/main/res/layout/table_structure_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"horizontal\" >\n\n    <TextView\n        android:id=\"@+id/text_1\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"50dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n    <TextView\n        android:id=\"@+id/text_2\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"50dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n    <TextView\n        android:id=\"@+id/text_3\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"50dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n    <TextView\n        android:id=\"@+id/text_4\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"50dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n    <TextView\n        android:id=\"@+id/text_5\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"50dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n    <TextView\n        android:id=\"@+id/text_6\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"50dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"6dip\" />\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/table_structure_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\" >\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"#8e8d8e\"\n        android:orientation=\"horizontal\" >\n\n        <TextView\n            android:id=\"@+id/text_1\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"25dp\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"6dip\"\n            android:text=\"@string/column_name\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/text_2\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"25dp\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"6dip\"\n            android:text=\"@string/column_type\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/text_3\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"25dp\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"6dip\"\n            android:text=\"@string/column_nullable\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/text_4\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"25dp\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"6dip\"\n            android:text=\"@string/column_unique\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/text_5\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"25dp\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"6dip\"\n            android:text=\"@string/column_default\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/text_6\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"25dp\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"6dip\"\n            android:text=\"@string/column_index\"\n            android:textColor=\"#fff\" />\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/table_structure_listview\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" >\n    </ListView>\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/update_sample_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:padding=\"10dp\" >\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/sample_codes\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_first_line\"\n                    android:textSize=\"18sp\" />\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:singleLine=\"true\" >\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_second_line_pre\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/singer_name_edit\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"none\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/justin_bieber\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_second_line_end\"\n                        android:textSize=\"18sp\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" >\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_third_line_pre\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/singer_age_edit\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"number\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/twenty\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_third_line_end\"\n                        android:textSize=\"18sp\" />\n                </LinearLayout>\n            </LinearLayout>\n        </HorizontalScrollView>\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_update_first_line_pre\"\n                    android:textSize=\"18sp\" />\n\n                <EditText\n                    android:id=\"@+id/singer_id_edit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:background=\"@null\"\n                    android:inputType=\"number\"\n                    android:singleLine=\"true\"\n                    android:text=\"@string/one\"\n                    android:textSize=\"18sp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_update_first_line_end\"\n                    android:textSize=\"18sp\" />\n            </LinearLayout>\n        </HorizontalScrollView>\n\n        <Button\n            android:id=\"@+id/update_btn1\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/run_codes_above\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/textfield_default\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/sample_codes_update_second_line\"\n                    android:textSize=\"18sp\" />\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" >\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_update_third_line_pre\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/name_to_update\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"none\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/taylor_swift\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_update_third_line_mid\"\n                        android:textSize=\"18sp\" />\n\n                    <EditText\n                        android:id=\"@+id/age_to_update\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:background=\"@null\"\n                        android:inputType=\"number\"\n                        android:singleLine=\"true\"\n                        android:text=\"@string/twenty_five\"\n                        android:textSize=\"18sp\" />\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/sample_codes_update_third_line_end\"\n                        android:textSize=\"18sp\" />\n                </LinearLayout>\n            </LinearLayout>\n        </HorizontalScrollView>\n\n        <Button\n            android:id=\"@+id/update_btn2\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/run_codes_above\" />\n\n        <HorizontalScrollView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"fill_parent\"\n            android:fadingEdge=\"none\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" >\n\n            <ListView\n                android:id=\"@+id/data_list_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:fadingEdge=\"none\"\n                android:overScrollMode=\"never\"\n                android:scrollbars=\"none\" >\n            </ListView>\n        </HorizontalScrollView>\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/progress_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\" />\n\n</RelativeLayout>"
  },
  {
    "path": "sample/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values/strings.xml",
    "content": "<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<resources>\n\n    <string name=\"app_name\">LitePal Sample</string>\n    <string name=\"manage_table_sample\">Manage Tables Sample</string>\n    <string name=\"crud_sample\">CRUD Sample</string>\n    <string name=\"manage_table\">Manage Tables</string>\n    <string name=\"crud\">CRUD</string>\n    <string name=\"aggregate_functions\">Aggregate Functions</string>\n    <string name=\"aggregate_sample\">Aggregate Sample</string>\n    <string name=\"table_generation_tip\">Tables will be generated automatically depending on the current model structure once you operate database.</string>\n    <string name=\"current_model_structure\">Current Model Structure</string>\n    <string name=\"current_table_structure\">Current Table Structure</string>\n    <string name=\"field_name\">Field Name</string>\n    <string name=\"field_type\">Field Type</string>\n    <string name=\"column_name\">Name</string>\n    <string name=\"column_type\">Type</string>\n    <string name=\"column_nullable\">Nullable</string>\n    <string name=\"column_unique\">Unique</string>\n    <string name=\"column_default\">Default</string>\n    <string name=\"column_index\">Index</string>\n    <string name=\"sample_codes\">Sample codes:</string>\n    <string name=\"result\">Result:</string>\n    <string name=\"save_sample\">Save Sample</string>\n    <string name=\"update_sample\">Update Sample</string>\n    <string name=\"delete_sample\">Delete Sample</string>\n    <string name=\"query_sample\">Query Sample</string>\n    <string name=\"count_sample\">Count Sample</string>\n    <string name=\"max_sample\">Max Sample</string>\n    <string name=\"min_sample\">Min Sample</string>\n    <string name=\"average_sample\">Average Sample</string>\n    <string name=\"sum_sample\">Sum Sample</string>\n    <string name=\"run_codes_above\">Run codes above</string>\n    <string name=\"sample_codes_first_line\">Singer singer = new Singer&#040;&#041;&#059;</string>\n    <string name=\"sample_codes_second_line_pre\">singer.setName&#040;\\&#034;</string>\n    <string name=\"sample_codes_second_line_end\">\\&#034;&#041;&#059;</string>\n    <string name=\"sample_codes_third_line_pre\">singer.setAge&#040;</string>\n    <string name=\"sample_codes_third_line_end\">&#041;&#059;</string>\n    <string name=\"sample_codes_fourth_line_pre\">singer.setMale&#040;</string>\n    <string name=\"sample_codes_fourth_line_end\">&#041;&#059;</string>\n    <string name=\"sample_codes_save\">singer.save&#040;&#041;&#059;</string>\n    <string name=\"sample_codes_update_first_line_pre\">singer.update&#040;</string>\n    <string name=\"sample_codes_update_first_line_end\">&#041;&#059;</string>\n    <string name=\"sample_codes_update_second_line\">singer.updateAll&#040;\\&#034;name=? and age=?\\&#034;,</string>\n    <string name=\"sample_codes_update_third_line_pre\">\\&#034;</string>\n    <string name=\"sample_codes_update_third_line_mid\">\\&#034;, \\&#034;</string>\n    <string name=\"sample_codes_update_third_line_end\">\\&#034;&#041;&#059;</string>\n    <string name=\"sample_codes_delete_first_line_pre\">DataSupport.delete&#040;Singer.class,&#160;</string>\n    <string name=\"sample_codes_delete_first_line_end\">&#041;&#059;</string>\n    <string name=\"sample_codes_delete_second_line\">DataSupport.deleteAll&#040;Singer.class,</string>\n    <string name=\"sample_codes_delete_third_line\">\\&#034;name = ? and age = ?\\&#034;,</string>\n    <string name=\"sample_codes_delete_fourth_line_pre\">\\&#034;</string>\n    <string name=\"sample_codes_delete_fourth_line_mid\">\\&#034;, \\&#034;</string>\n    <string name=\"sample_codes_delete_fourth_line_end\">\\&#034;&#041;&#059;</string>\n    <string name=\"sample_desc_query_first_line\">Find singer whose id is 1:</string>\n    <string name=\"sample_codes_query_first_line\">Singer singer = DataSupport.find&#040;\\nSinger.class, 1&#041;&#059;</string>\n    <string name=\"sample_desc_query_second_line\">Find all singers:</string>\n    <string name=\"sample_codes_query_second_line\">List&#60;Singer&#62; allSingers =\\nDataSupport.findAll&#040;Singer.class&#041;&#059;</string>\n    <string name=\"sample_desc_query_third_line\">Find all singers who are yonger than 20:</string>\n    <string name=\"sample_codes_query_third_line\">List&#60;Singer&#62; singers =\\nDataSupport.where&#040;\\&#034;age &#60; ?\\&#034;, \\&#034;20\\&#034;&#041;.\\nfind&#040;Singer.class&#041;&#059;</string>\n    <string name=\"sample_desc_query_fourth_line\">Find all female singers who are elder than 25:</string>\n    <string name=\"sample_codes_query_fourth_line\">List&#60;Singer&#62; singers =\\nDataSupport.where&#040;\\n\\&#034;age &#60; ? and ismale = ?\\&#034;, \\&#034;25\\&#034;, \\&#034;0\\&#034;&#041;.\\nfind&#040;Singer.class&#041;&#059;</string>\n    <string name=\"sample_desc_query_fifth_line\">Find same records as above by SQL:</string>\n    <string name=\"sample_codes_query_fifth_line\">Cursor cursor =\\nDataSupport.findBySQL&#040;\\n\\&#034;select * from singer where\\nage &#60; ? and ismale = ?\\&#034;, \\&#034;25\\&#034;, \\&#034;0\\&#034;&#041;&#059;</string>\n    <string name=\"sample_codes_count_first_line\">DataSupport.count&#040;Singer.class&#041;&#059;</string>\n    <string name=\"sample_codes_count_second_line_pre\">DataSupport.where&#040;\\&#034;age &#62; ?\\&#034;, \\&#034;</string>\n    <string name=\"sample_codes_count_second_line_end\">\\&#034;&#041;.</string>\n    <string name=\"sample_codes_count_third_line\">count&#040;Singer.class&#041;&#059;</string>\n    <string name=\"sample_codes_max_first_line\">DataSupport.max&#040;Singer.class,\\n\\&#034;age\\&#034;, Integer.TYPE&#041;&#059;</string>\n    <string name=\"sample_codes_max_second_line_pre\">DataSupport.where&#040;\\&#034;age &#60; ?\\&#034;, \\&#034;</string>\n    <string name=\"sample_codes_max_second_line_end\">\\&#034;&#041;.</string>\n    <string name=\"sample_codes_max_third_line\">max&#040;Singer.class, \\&#034;age\\&#034;,\\nInteger.TYPE&#041;&#059;</string>\n    <string name=\"sample_codes_min_first_line\">DataSupport.min&#040;Singer.class,\\n\\&#034;age\\&#034;, Integer.TYPE&#041;&#059;</string>\n    <string name=\"sample_codes_min_second_line_pre\">DataSupport.where&#040;\\&#034;age &#62; ?\\&#034;, \\&#034;</string>\n    <string name=\"sample_codes_min_second_line_end\">\\&#034;&#041;.</string>\n    <string name=\"sample_codes_min_third_line\">min&#040;Singer.class, \\&#034;age\\&#034;,\\nInteger.TYPE&#041;&#059;</string>\n    <string name=\"sample_codes_avg_first_line\">DataSupport.average&#040;Singer.class,\\n\\&#034;age\\&#034;&#041;&#059;</string>\n    <string name=\"sample_codes_avg_second_line_pre\">DataSupport.where&#040;\\&#034;age &#62; ?\\&#034;, \\&#034;</string>\n    <string name=\"sample_codes_avg_second_line_end\">\\&#034;&#041;.</string>\n    <string name=\"sample_codes_avg_third_line\">average&#040;Singer.class, \\&#034;age\\&#034;&#041;&#059;</string>\n    <string name=\"sample_codes_sum_first_line\">DataSupport.sum&#040;Singer.class,\\n\\&#034;age\\&#034;, Integer.TYPE&#041;&#059;</string>\n    <string name=\"sample_codes_sum_second_line_pre\">DataSupport.where&#040;\\&#034;age &#62; ?\\&#034;, \\&#034;</string>\n    <string name=\"sample_codes_sum_second_line_end\">\\&#034;&#041;.</string>\n    <string name=\"sample_codes_sum_third_line\">sum&#040;Singer.class, \\&#034;age\\&#034;,\\nInteger.TYPE&#041;&#059;</string>\n    <string name=\"taylor_swift\">Taylor Swift</string>\n    <string name=\"justin_bieber\">Justin Bieber</string>\n    <string name=\"one\">1</string>\n    <string name=\"twenty_five\">25</string>\n    <string name=\"twenty_three\">23</string>\n    <string name=\"twenty\">20</string>\n    <string name=\"boolean_false\">false</string>\n    <string name=\"boolean_true\">true</string>\n    <string name=\"error_param_is_not_valid\">Param is not valid.</string>\n    <string name=\"number_of_rows_affected\">%1$s rows affected.</string>\n\n</resources>"
  },
  {
    "path": "sample/src/main/res/values/styles.xml",
    "content": "<!--\n   Copyright (C) Tony Green, Litepal Framework Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n \n        http://www.apache.org/licenses/LICENSE-2.0\n \n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n-->\n<resources>\n\n    <!--\n        Base application theme, dependent on API level. This theme is replaced\n        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Light\">\n        <!--\n            Theme customizations available in newer API levels can go in\n            res/values-vXX/styles.xml, while customizations related to\n            backward-compatibility can go here.\n        -->\n    </style>\n\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n\n    </style>\n\n</resources>\n"
  },
  {
    "path": "settings.gradle",
    "content": "include 'sample', 'core'"
  }
]