diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 000000000..715a11e59 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,4 @@ +# .github + + + diff --git a/.github/CONTRIBUTING.md b/.github/contributing.md similarity index 77% rename from .github/CONTRIBUTING.md rename to .github/contributing.md index 2ad4c337b..e232eaa7b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/contributing.md @@ -1,14 +1,15 @@ -Contributing Guidelines --------------------------------------------------- +# CONTRIBUTING + +## Contributing Guidelines This document provides general guidelines about how to contribute to the project. Keep in mind these important things before you start contributing. ## Reporting issues -* Use [github issues](https://github.com/Raizlabs/DBFlow/issues) to report a bug. +* Use [github issues](https://github.com/agrosner/DBFlow/issues) to report a bug. * Before creating a new issue: - * Make sure you are using the [latest release](https://github.com/Raizlabs/DBFlow/releases). - * Check if the issue was [already reported or fixed](https://github.com/Raizlabs/DBFlow/issues?utf8=%E2%9C%93&q=is%3Aissue). Notice that it may not be released yet. + * Make sure you are using the [latest release](https://github.com/agrosner/DBFlow/releases). + * Check if the issue was [already reported or fixed](https://github.com/agrosner/DBFlow/issues?utf8=✓&q=is%3Aissue). Notice that it may not be released yet. * If you found a match add the github "+1" reaction brief comment. This helps prioritize the issues addressing the most common and critical ones first. If possible, add additional information to help us reproduce, and find the issue. Please use your best judgement. * Reporting issues: * Please include the following information to help maintainers to fix the problem faster: @@ -16,15 +17,9 @@ This document provides general guidelines about how to contribute to the project * Full console output of stack trace or code compilation error. * Any other additional detail you think it would be useful to understand and solve the problem. - ## Pull requests -I welcome and encourage all pull requests. It usually will take me within 24-48 hours to respond to any issue or request. Here are some basic rules to follow to ensure timely addition of your request: - 1. Match coding style (braces, spacing, etc.) This is best achieved using CMD+Option+L (Reformat code) on Mac (not sure for Windows) with Android Studio defaults. - 2. If its a feature, bugfix, or anything please only change code to what you specify. - 3. Please keep PR titles easy to read and descriptive of changes, this will make them easier to merge :) - 4. Pull requests _must_ be made against `develop` branch. Any other branch (unless specified by the maintainers) will get rejected. - +I welcome and encourage all pull requests. It usually will take me within 24-48 hours to respond to any issue or request. Here are some basic rules to follow to ensure timely addition of your request: 1. Match coding style \(braces, spacing, etc.\) This is best achieved using CMD+Option+L \(Reformat code\) on Mac \(not sure for Windows\) with Android Studio defaults. 2. If its a feature, bugfix, or anything please only change code to what you specify. 3. Please keep PR titles easy to read and descriptive of changes, this will make them easier to merge :\) 4. Pull requests _must_ be made against `develop` branch. Any other branch \(unless specified by the maintainers\) will get rejected. ### Suggested git workflow to contribute @@ -39,12 +34,12 @@ I welcome and encourage all pull requests. It usually will take me within 24-48 9. Push your branch into your forked remote repository. 10. Create a new pull request adding any useful comment. - ### Feature proposal We would love to hear your ideas and make discussions about it. * Use github issues to make feature proposals. -* We use `type: feature request` label to mark all [feature request issues](https://github.com/Raizlabs/DBFlow/labels/type%3A%20feature%20request). +* We use `type: feature request` label to mark all [feature request issues](https://github.com/agrosner/DBFlow/labels/type%3A%20feature%20request). * Before submitting your proposal make sure there is no similar feature request. If you find a match, feel free to join the discussion or just or just act with a reaction if you think the feature is worth implementing. -* Be as specific as possible providing a precise explanation of the feature so anyone can understand the problem and the benefits of solving it. \ No newline at end of file +* Be as specific as possible providing a precise explanation of the feature so anyone can understand the problem and the benefits of solving it. + diff --git a/README.md b/README.md index e9e018333..e3e682cb8 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,98 @@ +# README + ![Image](https://github.com/agrosner/DBFlow/blob/develop/dbflow_banner.png?raw=true) -[![JitPack.io](https://img.shields.io/badge/JitPack.io-4.2.3-red.svg?style=flat)](https://jitpack.io/#Raizlabs/DBFlow) [![Android Weekly](http://img.shields.io/badge/Android%20Weekly-%23129-2CB3E5.svg?style=flat)](http://androidweekly.net/issues/issue-129) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-DBFlow-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1134) +[![JitPack.io](https://img.shields.io/badge/JitPack.io-5.0.0alpha1-red.svg?style=flat)](https://jitpack.io/#Raizlabs/DBFlow) [![Android Weekly](http://img.shields.io/badge/Android%20Weekly-%23129-2CB3E5.svg?style=flat)](http://androidweekly.net/issues/issue-129) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-DBFlow-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1134) + +DBFlow is fast, efficient, and feature-rich Kotlin database library built on SQLite for Android. DBFlow utilizes annotation processing to generate SQLite boilerplate for you and provides a powerful SQLite query language that makes using SQLite a joy. + +DBFlow is built from a collection of some of the best features of many database libraries. Don't let an ORM or library get in your way, let the code you write in your applications be the best as possible. + +Supports: + +**Kotlin:** Built using the language, the library is super-concise, null-safe and efficient. + +**Coroutines:** Adds coroutine support for queries. -A robust, powerful, and very simple ORM android database library with **annotation processing**. +**RX Java:** Enable applications to be reactive by listening to DB changes and ensuring your subscribers are up-to-date. -The library is built on speed, performance, and approachability. It not only eliminates most boiler-plate code for dealing with databases, but also provides a powerful and simple API to manage interactions. +**Paging:** Android architecture component paging library support for queries via `QueryDataSource`. -Let DBFlow make SQL code _flow_ like a _steady_ stream so you can focus on writing amazing apps. +**SQLCipher:** Easy database encryption support in this library. -# Why Use DBFlow -DBFlow is built from a collection of the best features of many database libraries in the most efficient way possible. Also, it is built to not only make it _significantly_ easier to deal with databases on Android, but also to provide extensibility. Don't let an ORM or library get in your way, let the code you write in your applications be the best as possible. -- **Extensibility**: No restrictions on inheritance of your table classes. They can be plain POJOs, no subclass required, but as a convenience we recommend using `BaseModel`. You can extend non-`Model` classes in different packages and use them as your DB tables. Also you can subclass other tables to join the `@Column` together, and again they can be in different packages. -- **Speed**: Built with java's annotation processing code generation, there's almost zero runtime performance hit by using this library (only reflection is creation of the main, generated database module's constructor). This library saves hours of boilerplate code and maintenance by generating the code for you. With powerful model caching (multiple primary key `Model` too), you can surpass the speed of SQLite by reusing where possible. We have support for lazy-loading relationships on-demand such as `@ForeignKey` or `@OneToMany` that make queries happen super-fast. -- **SQLite Query Flow**: The queries in this library adhere as closely as possible to SQLite native queries. `select(name, screenSize).from(Android.class).where(name.is("Nexus 5x")).and(version.is(6.0)).querySingle()` -- **Open Source**: This library is fully open source and contributions are not only welcomed, but encouraged. -- **Robust**: We support `Trigger`, `ModelView`, `Index`, `Migration`, built-in ways to manage database access, and many more features. SQLCipher, RXJava, and more! -- **Multiple Databases, Multiple Modules**: we seamlessly support multiple database files, database modules using DBFlow in other dependencies, simultaneously. -- **Built On SQLite**: SQLite is the most widely used database engine in world and using it as your base, you are not tied to a limited set of platforms or libraries. +**SQLite Query Language:** Enabling autocompletion on sqlite queries combined with Kotlin language features means SQLite-like syntax. -# Changelog +## Changelog Changes exist in the [releases tab](https://github.com/Raizlabs/DBFlow/releases). -# Usage Docs +## Usage Docs + For more detailed usage, check out it out [here](https://agrosner.gitbooks.io/dbflow/content/) -# Including in your project +## Including in your project -```groovy +Add jitpack.io to your project's repositories: +```groovy allProjects { repositories { + google() // required to find the project's artifacts + // place last maven { url "https://www.jitpack.io" } } } ``` -Add the library to the project-level build.gradle, using the apt plugin to enable Annotation Processing: +Add artifacts to your project: ```groovy + apply plugin: 'kotlin-kapt' // only required for kotlin consumers. - apply plugin: 'kotlin-kapt' // required for kotlin. - - def dbflow_version = "4.2.4" - // or dbflow_version = "develop-SNAPSHOT" for grabbing latest dependency in your project on the develop branch + def dbflow_version = "5.0.0-alpha1" // or 10-digit short-hash of a specific commit. (Useful for bugs fixed in develop, but not in a release yet) dependencies { - // if Java use this. If using Kotlin do NOT use this. - annotationProcessor "com.github.Raizlabs.DBFlow:dbflow-processor:${dbflow_version}" - // Use if Kotlin user. - kapt "com.github.Raizlabs.DBFlow:dbflow-processor:${dbflow_version}" + kapt "com.github.agrosner.dbflow:processor:${dbflow_version}" - compile "com.github.Raizlabs.DBFlow:dbflow-core:${dbflow_version}" - compile "com.github.Raizlabs.DBFlow:dbflow:${dbflow_version}" + // Annotation Processor + // if only using Java, use this. If using Kotlin do NOT use this. + annotationProcessor "com.github.agrosner.dbflow:processor:${dbflow_version}" + + + // core set of libraries + compile "com.github.agrosner.dbflow:core:${dbflow_version}" + compile "com.github.agrosner.dbflow:lib:${dbflow_version}" // sql-cipher database encryption (optional) - compile "com.github.Raizlabs.DBFlow:dbflow-sqlcipher:${dbflow_version}" + compile "com.github.agrosner.dbflow:sqlcipher:${dbflow_version}" compile "net.zetetic:android-database-sqlcipher:${sqlcipher_version}@aar" - // kotlin extensions - compile "com.github.Raizlabs.DBFlow:dbflow-kotlinextensions:${dbflow_version}" - - // RXJava 1 support - compile "com.github.Raizlabs.DBFlow:dbflow-rx:${dbflow_version}" + // RXJava 2 support + compile "com.github.agrosner.dbflow:reactive-streams:${dbflow_version}" - // RXJava 1 Kotlin Extensions Support - compile "com.github.Raizlabs.DBFlow:dbflow-rx-kotlinextensions:${dbflow_version}" + // Kotlin Coroutines + compile "com.github.agrosner.dbflow:coroutines:${dbflow_version}" - // RXJava 2 support - compile "com.github.Raizlabs.DBFlow:dbflow-rx2:${dbflow_version}" + // Android Architecture Components Paging Library Support + compile "com.github.agrosner.dbflow:paging:${dbflow_version}" - // RXJava 2 Kotlin Extensions Support - compile "com.github.Raizlabs.DBFlow:dbflow-rx2-kotlinextensions:${dbflow_version}" + // adds generated content provider annotations + support. + compile "com.github.agrosner.dbflow:contentprovider:${dbflow_version}" } - ``` -# Pull Requests -I welcome and encourage all pull requests. Please read over these guidelines to ensure smooth PR attention: - 1. Match coding style (braces, spacing, etc.) This is best achieved using **Reformat Code** shortcut, command+option+L on Mac and Ctrl+Alt+L on Windows, with Android Studio defaults. - 2. If its a feature, bugfix, or anything please only change code to what you specify. - 3. Please keep PR titles easy to read and descriptive of changes, this will make them easier to merge :) - 4. Pull requests _must_ be made against `develop` branch. Any other branch (unless specified by the maintainers) will get rejected. - 5. Have fun! +## Pull Requests + +I welcome and encourage all pull requests. Here are some basic rules to follow to ensure timely addition of your request: 1. Match coding style \(braces, spacing, etc.\) This is best achieved using **Reformat Code** shortcut, command+option+L on Mac and Ctrl+Alt+L on Windows, with Android Studio defaults. 2. If its a feature, bugfix, or anything please only change code to what you specify. 3. Please keep PR titles easy to read and descriptive of changes, this will make them easier to merge :\) 4. Pull requests _must_ be made against `develop` branch. Any other branch \(unless specified by the maintainers\) will get **rejected**. 5. Have fun! + +## Maintainer + +Originally created by [Raizlabs](https://www.raizlabs.com), a [Rightpoint](https://www.rightpoint.com) company + +Maintained by [agrosner](https://github.com/agrosner) \([@agrosner](https://www.twitter.com/agrosner)\) -# Maintained By -[agrosner](https://github.com/agrosner) ([@agrosner](https://www.twitter.com/agrosner)) diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 000000000..93783a91d --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,32 @@ +# Table of contents + +* [README](README.md) +* [GettingStarted](gettingstarted.md) +* [Usage Docs](usage2/README.md) + * [Including In Project](usage2/including-in-project.md) + * [Proguard](usage2/proguard.md) + * [Main Usage](usage2/usage/README.md) + * [Databases](usage2/usage/databases.md) + * [Models](usage2/usage/models.md) + * [Migrations](usage2/usage/migrations.md) + * [Views](usage2/usage/modelviews.md) + * [Relationships](usage2/usage/relationships.md) + * [Storing Data](usage2/usage/storingdata.md) + * [Retrieval](usage2/usage/retrieval.md) + * [SQLite Query Language](usage2/usage/sqlitewrapperlanguage.md) + * [TypeConverters](usage2/usage/typeconverters.md) + * [Observability](usage2/usage/observability.md) + * [RXJavaSupport](usage2/rxjavasupport.md) + * [Advanced Usage](usage2/advanced-usage/README.md) + * [Caching](usage2/advanced-usage/caching.md) + * [ListBasedQueries](usage2/advanced-usage/listbasedqueries.md) + * [MultipleModules](usage2/advanced-usage/multiplemodules.md) + * [QueryModels](usage2/advanced-usage/querymodels.md) + * [Indexing](usage2/advanced-usage/indexing.md) + * [SQLCipher](usage2/advanced-usage/sqlciphersupport.md) + * [ContentProviderGeneration](usage2/contentprovidergeneration.md) + * [Migration4Guide](usage2/migration4guide.md) +* [ISSUE\_TEMPLATE](issue_template.md) +* [.github](.github/README.md) + * [CONTRIBUTING](.github/contributing.md) + diff --git a/android-artifacts.gradle b/android-artifacts.gradle deleted file mode 100644 index 9c8dae956..000000000 --- a/android-artifacts.gradle +++ /dev/null @@ -1,43 +0,0 @@ -apply plugin: 'com.github.dcendents.android-maven' -install { - repositories.mavenInstaller { - pom { - project { - packaging bt_packaging - name bt_name - url bt_siteUrl - licenses { - license { - name bt_licenseName - url bt_licenseUrl - } - } - scm { - connection bt_gitUrl - developerConnection bt_gitUrl - url bt_siteUrl - } - } - } - } -} - -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -task javadoc(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - //archives javadocJar - archives sourcesJar -} \ No newline at end of file diff --git a/bintray_upload.gradle b/bintray_upload.gradle deleted file mode 100644 index d65988aad..000000000 --- a/bintray_upload.gradle +++ /dev/null @@ -1,30 +0,0 @@ -/** - * This task will generate the pom.xml file and install the AAR to the local M2 repo - */ -install { - repositories { - mavenInstaller { - pom.artifactId = bt_artifact_id - pom { - project { - packaging bt_packaging - name bt_name - url bt_siteUrl - - licenses { - license { - name bt_licenseName - url bt_licenseUrl - } - } - - scm { - connection bt_gitUrl - developerConnection bt_gitUrl - url bt_siteUrl - } - } - } - } - } -} diff --git a/build.gradle b/build.gradle index 977b2d77e..103a7aa0f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,33 @@ buildscript { - ext.kotlin_version = '1.1.51' + ext { + kotlin_version = '1.2.71' + target_sdk = 28 + min_sdk = 7 + min_sdk_rx = 15 + sql_cipher_min = 7 + arch_min = 14 + + deps = [ + kotlin : "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}", + sql_cipher : "net.zetetic:android-database-sqlcipher:3.5.9@aar", + rx2 : 'io.reactivex.rxjava2:rxjava:2.2.2', + coroutines : "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.26.1", + javapoet : 'com.squareup:javapoet:1.11.1', + kpoet : 'com.github.agrosner:KPoet:1.0.0', + javax_annotation : 'org.glassfish:javax.annotation:10.0-b28', + junit : 'junit:junit:4.12', + paging : "android.arch.paging:runtime:1.0.0", + support_annotations: "androidx.annotation:annotation:1.0.0", + ] + } repositories { jcenter() google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' - classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.7.3' + classpath 'com.android.tools.build:gradle:3.3.0-beta01' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/dbflow-core/.gitignore b/contentprovider-annotations/.gitignore similarity index 100% rename from dbflow-core/.gitignore rename to contentprovider-annotations/.gitignore diff --git a/contentprovider-annotations/build.gradle b/contentprovider-annotations/build.gradle new file mode 100644 index 000000000..5c403ec64 --- /dev/null +++ b/contentprovider-annotations/build.gradle @@ -0,0 +1,19 @@ +apply plugin: 'kotlin' + +project.ext.artifactId = bt_name + +targetCompatibility = JavaVersion.VERSION_1_8 +sourceCompatibility = JavaVersion.VERSION_1_8 + +dependencies { + compile deps.kotlin + compile project(':core') +} + +apply from: '../kotlin-artifacts.gradle' + +compileKotlin { + kotlinOptions { + apiVersion = "1.2" + } +} \ No newline at end of file diff --git a/contentprovider-annotations/gradle.properties b/contentprovider-annotations/gradle.properties new file mode 100644 index 000000000..ec906f2d6 --- /dev/null +++ b/contentprovider-annotations/gradle.properties @@ -0,0 +1,3 @@ +bt_name=contentprovider-annotations +bt_packaging=jar +bt_artifact_id=contentprovider-annotations \ No newline at end of file diff --git a/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/ContentProvider.kt b/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/ContentProvider.kt new file mode 100644 index 000000000..9446b01ae --- /dev/null +++ b/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/ContentProvider.kt @@ -0,0 +1,22 @@ +package com.dbflow5.contentprovider.annotation + +import kotlin.reflect.KClass + +/** + * Description: Defines a Content Provider that gets generated. + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +@Retention(AnnotationRetention.SOURCE) +annotation class ContentProvider( + /** + * @return The authority URI for this provider. + */ + val authority: String, + /** + * @return The class of the database this belongs to + */ + val database: KClass<*>, + /** + * @return The base content uri String to use for all paths + */ + val baseContentUri: String = "") diff --git a/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/ContentUri.kt b/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/ContentUri.kt new file mode 100644 index 000000000..da0ad2d6a --- /dev/null +++ b/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/ContentUri.kt @@ -0,0 +1,61 @@ +package com.dbflow5.contentprovider.annotation + +/** + * Description: Defines the URI for a content provider. + */ +@Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Retention(AnnotationRetention.SOURCE) +annotation class ContentUri( + /** + * @return the path of this ContentUri. ex: notes/#, notes/1, etc. Must be unique within a [TableEndpoint] + */ + val path: String, + /** + * @return The type of content that this uri is associated with. Ex: [ContentType.VND_SINGLE] + */ + val type: String, + /** + * @return If the path defines "#", then we use these numbers to find them in the same order as + * where column. + */ + val segments: Array = [], + /** + * @return false if you wish to not allow queries from the specified URI. + */ + val queryEnabled: Boolean = true, + /** + * @return false if you wish to prevent inserts. + */ + val insertEnabled: Boolean = true, + /** + * @return false if you wish to prevent deletion. + */ + val deleteEnabled: Boolean = true, + /** + * @return false if you wish to prevent updates. + */ + val updateEnabled: Boolean = true) + +/** + * Provides some handy constants for defining a [.type] + */ +object ContentType { + + const val VND_MULTIPLE = "vnd.android.cursor.dir/" + + const val VND_SINGLE = "vnd.android.cursor.item/" +} + +/** + * Defines the path segment that we use when we specify "#" in the path. + */ +annotation class PathSegment( + /** + * @return The number segment this corresponds to. + */ + val segment: Int, + /** + * @return The column name that this segment will use. + */ + val column: String) \ No newline at end of file diff --git a/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/Notify.kt b/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/Notify.kt new file mode 100644 index 000000000..fc6c0d257 --- /dev/null +++ b/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/Notify.kt @@ -0,0 +1,25 @@ +package com.dbflow5.contentprovider.annotation + +/** + * Description: Annotates a method part of [com.dbflow5.annotation.provider.TableEndpoint] + * that gets called back when changed. The method must return a Uri or an array of Uri[] to notify changed on + * the content provider. + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +annotation class Notify( + /** + * @return The [com.dbflow5.annotation.provider.Notify.Method] notify + */ + val notifyMethod: NotifyMethod, + /** + * @return Registers itself for the following paths. If a specific path is called for the specified + * method, the method this annotation corresponds to will be called. + */ + val paths: Array = []) + +enum class NotifyMethod { + INSERT, + UPDATE, + DELETE +} \ No newline at end of file diff --git a/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/TableEndpoint.kt b/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/TableEndpoint.kt new file mode 100644 index 000000000..cc3b48b24 --- /dev/null +++ b/contentprovider-annotations/src/main/kotlin/com/dbflow5/contentprovider/annotation/TableEndpoint.kt @@ -0,0 +1,18 @@ +package com.dbflow5.contentprovider.annotation + +import kotlin.reflect.KClass + +/** + * Description: Defines an endpoint that gets placed inside of a [ContentProvider] + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +@Retention(AnnotationRetention.SOURCE) +annotation class TableEndpoint( + /** + * @return The name of the table this endpoint corresponds to. + */ + val name: String, + /** + * @return When placed in a top-level class, this is required to connect it to a provider. + */ + val contentProvider: KClass<*>) diff --git a/dbflow-kotlinextensions/.gitignore b/contentprovider/.gitignore similarity index 100% rename from dbflow-kotlinextensions/.gitignore rename to contentprovider/.gitignore diff --git a/contentprovider/build.gradle b/contentprovider/build.gradle new file mode 100644 index 000000000..97f0317a8 --- /dev/null +++ b/contentprovider/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +project.ext.artifactId = bt_name + +android { + compileSdkVersion target_sdk + + defaultConfig { + minSdkVersion min_sdk + targetSdkVersion target_sdk + } + + lintOptions { + abortOnError false + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } +} + +dependencies { + api project(':contentprovider-annotations') + api project(':lib') + api deps.support_annotations + api "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" +} + +apply from: '../kotlin-artifacts.gradle' + diff --git a/contentprovider/gradle.properties b/contentprovider/gradle.properties new file mode 100644 index 000000000..1802744c1 --- /dev/null +++ b/contentprovider/gradle.properties @@ -0,0 +1,3 @@ +bt_name=contentprovider +bt_packaging=aar +bt_artifact_id=contentprovider \ No newline at end of file diff --git a/contentprovider/proguard-rules.pro b/contentprovider/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/contentprovider/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/contentprovider/src/main/AndroidManifest.xml b/contentprovider/src/main/AndroidManifest.xml new file mode 100644 index 000000000..2f5f61755 --- /dev/null +++ b/contentprovider/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseContentProvider.kt b/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseContentProvider.kt new file mode 100644 index 000000000..ade63b741 --- /dev/null +++ b/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseContentProvider.kt @@ -0,0 +1,53 @@ +package com.dbflow5.provider + +import android.content.ContentProvider +import android.content.ContentValues +import android.net.Uri +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.config.DatabaseHolder +import com.dbflow5.config.FlowManager +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.transaction.ITransaction + +/** + * Description: The base provider class that [com.dbflow5.annotation.provider.ContentProvider] + * extend when generated. + */ +abstract class BaseContentProvider +protected constructor(databaseHolderClass: Class? = null) : ContentProvider() { + + protected open var moduleClass: Class? = databaseHolderClass + + protected val database: DBFlowDatabase by lazy { FlowManager.getDatabase(databaseName) } + + protected abstract val databaseName: String + + override fun onCreate(): Boolean { + // If this is a module, then we need to initialize the module as part + // of the creation process. We can assume the framework has been general + // framework has been initialized. + moduleClass + ?.let { FlowManager.initModule(it) } + ?: context?.let { FlowManager.init(it) } + return true + } + + override fun bulkInsert(uri: Uri, values: Array): Int { + + val count = database.executeTransaction(object : ITransaction { + override fun execute(databaseWrapper: DatabaseWrapper): IntArray { + val count = intArrayOf(0) + for (contentValues in values) { + count[0] += bulkInsert(uri, contentValues) + } + return count + } + }) + + context?.contentResolver?.notifyChange(uri, null) + return count[0] + } + + protected abstract fun bulkInsert(uri: Uri, contentValues: ContentValues): Int + +} diff --git a/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseProviderModel.kt b/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseProviderModel.kt new file mode 100644 index 000000000..aa66e711a --- /dev/null +++ b/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseProviderModel.kt @@ -0,0 +1,70 @@ +package com.dbflow5.provider + +import android.content.ContentProvider +import com.dbflow5.config.FlowManager +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.OperatorGroup +import com.dbflow5.structure.BaseModel +import com.dbflow5.structure.Model + +/** + * Description: Provides a base implementation of a [Model] backed + * by a content provider. All model operations are overridden using the [ContentUtils]. + * Consider using a [BaseSyncableProviderModel] if you wish to + * keep modifications locally from the [ContentProvider] + */ +abstract class BaseProviderModel : BaseModel(), ModelProvider { + + override fun delete(wrapper: DatabaseWrapper): Boolean = ContentUtils.delete(FlowManager.context, deleteUri, this) > 0 + + override fun save(wrapper: DatabaseWrapper): Boolean { + val count = ContentUtils.update(FlowManager.context, updateUri, this) + return if (count == 0) { + insert(wrapper) > 0 + } else { + count > 0 + } + } + + override fun update(wrapper: DatabaseWrapper): Boolean + = ContentUtils.update(FlowManager.context, updateUri, this) > 0 + + override fun insert(wrapper: DatabaseWrapper): Long + = if (ContentUtils.insert(FlowManager.context, insertUri, this) != null) 1 else 0 + + /** + * Runs a query on the [ContentProvider] to see if it returns data. + * + * @return true if this model exists in the [ContentProvider] based on its primary keys. + */ + override fun exists(wrapper: DatabaseWrapper): Boolean { + val cursor = ContentUtils.query(FlowManager.context.contentResolver, + queryUri, modelAdapter.getPrimaryConditionClause(this), "") + val exists = cursor != null && cursor.count > 0 + cursor?.close() + return exists + } + + @Suppress("UNCHECKED_CAST") + override fun load(whereOperatorGroup: OperatorGroup, + orderBy: String?, + wrapper: DatabaseWrapper, + vararg columns: String?): T? { + val cursor = ContentUtils.query(FlowManager.context.contentResolver, + queryUri, whereOperatorGroup, orderBy, *columns) + if (cursor != null) { + val flowCursor = FlowCursor.from(cursor) + if (flowCursor.moveToFirst()) { + val model: T = modelAdapter.loadFromCursor(flowCursor, wrapper) as T + flowCursor.close() + return model + } + } + return null + } + + @Suppress("UNCHECKED_CAST") + override fun load(wrapper: DatabaseWrapper): T? = load(modelAdapter.getPrimaryConditionClause(this), "", wrapper) as T? + +} diff --git a/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseSyncableProviderModel.kt b/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseSyncableProviderModel.kt new file mode 100644 index 000000000..4f3b9a925 --- /dev/null +++ b/contentprovider/src/main/kotlin/com/dbflow5/provider/BaseSyncableProviderModel.kt @@ -0,0 +1,57 @@ +package com.dbflow5.provider + +import android.content.ContentProvider +import com.dbflow5.config.FlowManager +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.OperatorGroup +import com.dbflow5.structure.BaseModel +import com.dbflow5.structure.Model + +/** + * Description: Provides a base implementation of a [Model] backed + * by a content provider. All operations sync with the content provider in this app from a [ContentProvider] + */ +abstract class BaseSyncableProviderModel : BaseModel(), ModelProvider { + + override fun insert(wrapper: DatabaseWrapper): Long { + val rowId = super.insert(wrapper) + ContentUtils.insert(FlowManager.context, insertUri, this) + return rowId + } + + override fun save(wrapper: DatabaseWrapper): Boolean { + return if (exists(wrapper)) { + super.save(wrapper) && ContentUtils.update(FlowManager.context, updateUri, this) > 0 + } else { + super.save(wrapper) && ContentUtils.insert(FlowManager.context, insertUri, this) != null + } + } + + override fun delete(wrapper: DatabaseWrapper): Boolean + = super.delete(wrapper) && ContentUtils.delete(FlowManager.context, deleteUri, this) > 0 + + override fun update(wrapper: DatabaseWrapper): Boolean + = super.update(wrapper) && ContentUtils.update(FlowManager.context, updateUri, this) > 0 + + @Suppress("UNCHECKED_CAST") + override fun load(whereOperatorGroup: OperatorGroup, + orderBy: String?, + wrapper: DatabaseWrapper, + vararg columns: String?): T? { + val cursor = ContentUtils.query(FlowManager.context.contentResolver, + queryUri, whereOperatorGroup, orderBy, *columns) + cursor?.let { + val flowCursor = FlowCursor.from(cursor) + if (flowCursor.moveToFirst()) { + val model: T = modelAdapter.loadFromCursor(flowCursor, wrapper) as T + flowCursor.close() + return model + } + } + return null + } + + @Suppress("UNCHECKED_CAST") + override fun load(wrapper: DatabaseWrapper): T? = load(modelAdapter.getPrimaryConditionClause(this), "", wrapper) as T? +} diff --git a/contentprovider/src/main/kotlin/com/dbflow5/provider/ContentUtils.kt b/contentprovider/src/main/kotlin/com/dbflow5/provider/ContentUtils.kt new file mode 100644 index 000000000..87f092a1d --- /dev/null +++ b/contentprovider/src/main/kotlin/com/dbflow5/provider/ContentUtils.kt @@ -0,0 +1,313 @@ +package com.dbflow5.provider + +import android.content.ContentResolver +import android.content.ContentValues +import android.content.Context +import android.net.Uri +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.contentprovider.annotation.ContentProvider +import com.dbflow5.config.FlowLog +import com.dbflow5.config.modelAdapter +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.Operator +import com.dbflow5.query.OperatorGroup + +/** + * Description: Provides handy wrapper mechanisms for [android.content.ContentProvider] + */ +object ContentUtils { + + /** + * The default content URI that Android recommends. Not necessary, however. + */ + const val BASE_CONTENT_URI = "content://" + + /** + * Constructs an Uri with the [.BASE_CONTENT_URI] and authority. Add paths to append to the Uri. + * + * @param authority The authority for a [ContentProvider] + * @param paths The list of paths to append. + * @return A complete Uri for a [ContentProvider] + */ + @JvmStatic + fun buildUriWithAuthority(authority: String, vararg paths: String): Uri = + buildUri(BASE_CONTENT_URI, authority, *paths) + + /** + * Constructs an Uri with the specified baseContent uri and authority. Add paths to append to the Uri. + * + * @param baseContentUri The base content URI for a [ContentProvider] + * @param authority The authority for a [ContentProvider] + * @param paths The list of paths to append. + * @return A complete Uri for a [ContentProvider] + */ + @JvmStatic + fun buildUri(baseContentUri: String, authority: String, vararg paths: String): Uri { + val builder = Uri.parse(baseContentUri + authority).buildUpon() + for (path in paths) { + builder.appendPath(path) + } + return builder.build() + } + + /** + * Inserts the model into the [android.content.ContentResolver]. Uses the insertUri to resolve + * the reference and the model to convert its data into [android.content.ContentValues] + * + * @param insertUri A [android.net.Uri] from the [ContentProvider] class definition. + * @param model The model to insert. + * @return A Uri of the inserted data. + */ + @JvmStatic + fun insert(context: Context, insertUri: Uri, model: TableClass): Uri? = + insert(context.contentResolver, insertUri, model) + + /** + * Inserts the model into the [android.content.ContentResolver]. Uses the insertUri to resolve + * the reference and the model to convert its data into [android.content.ContentValues] + * + * @param contentResolver The content resolver to use + * @param insertUri A [android.net.Uri] from the [ContentProvider] class definition. + * @param model The model to insert. + * @return The Uri of the inserted data. + */ + @JvmStatic + fun insert(contentResolver: ContentResolver, insertUri: Uri, model: TableClass): Uri? { + val adapter = model.javaClass.modelAdapter + + val contentValues = ContentValues() + adapter.bindToInsertValues(contentValues, model) + val uri: Uri? = contentResolver.insert(insertUri, contentValues) + uri?.let { + adapter.updateAutoIncrement(model, uri.pathSegments[uri.pathSegments.size - 1].toLong()) + } + return uri + } + + /** + * Inserts the list of model into the [ContentResolver]. Binds all of the models to [ContentValues] + * and runs the [ContentResolver.bulkInsert] method. Note: if any of these use + * autoIncrementing primary keys the ROWID will not be properly updated from this method. If you care + * use [.insert] instead. + * + * @param contentResolver The content resolver to use + * @param bulkInsertUri The URI to bulk insert with + * @param table The table to insert into + * @param models The models to insert. + * @return The count of the rows affected by the insert. + */ + @JvmStatic + fun bulkInsert(contentResolver: ContentResolver, bulkInsertUri: Uri, + table: Class, models: List?): Int { + val contentValues = arrayListOf() + val adapter = table.modelAdapter + + if (models != null) { + for (i in contentValues.indices) { + val values = ContentValues() + adapter.bindToInsertValues(values, models[i]) + contentValues += values + } + } + return contentResolver.bulkInsert(bulkInsertUri, contentValues.toTypedArray()) + } + + /** + * Inserts the list of model into the [ContentResolver]. Binds all of the models to [ContentValues] + * and runs the [ContentResolver.bulkInsert] method. Note: if any of these use + * autoincrement primary keys the ROWID will not be properly updated from this method. If you care + * use [.insert] instead. + * + * @param bulkInsertUri The URI to bulk insert with + * @param table The table to insert into + * @param models The models to insert. + * @return The count of the rows affected by the insert. + */ + @JvmStatic + fun bulkInsert(context: Context, + bulkInsertUri: Uri, + table: Class, + models: List): Int = + bulkInsert(context.contentResolver, bulkInsertUri, table, models) + + /** + * Updates the model through the [android.content.ContentResolver]. Uses the updateUri to + * resolve the reference and the model to convert its data in [android.content.ContentValues] + * + * @param updateUri A [android.net.Uri] from the [ContentProvider] + * @param model A model to update + * @return The number of rows updated. + */ + @JvmStatic + fun update(context: Context, + updateUri: Uri, + model: TableClass): Int = + update(context.contentResolver, updateUri, model) + + /** + * Updates the model through the [android.content.ContentResolver]. Uses the updateUri to + * resolve the reference and the model to convert its data in [android.content.ContentValues] + * + * @param contentResolver The content resolver to use + * @param updateUri A [android.net.Uri] from the [ContentProvider] + * @param model The model to update + * @return The number of rows updated. + */ + @JvmStatic + fun update(contentResolver: ContentResolver, + updateUri: Uri, model: TableClass): Int { + val adapter = model.javaClass.modelAdapter + + val contentValues = ContentValues() + adapter.bindToContentValues(contentValues, model) + val count = contentResolver.update(updateUri, contentValues, + adapter.getPrimaryConditionClause(model).query, null) + if (count == 0) { + FlowLog.log(FlowLog.Level.W, "Updated failed of: " + model.javaClass) + } + return count + } + + /** + * Deletes the specified model through the [android.content.ContentResolver]. Uses the deleteUri + * to resolve the reference and the model to [ModelAdapter.getPrimaryConditionClause]} + * + * @param deleteUri A [android.net.Uri] from the [ContentProvider] + * @param model The model to delete + * @return The number of rows deleted. + */ + @JvmStatic + fun delete(context: Context, deleteUri: Uri, model: TableClass): Int = + delete(context.contentResolver, deleteUri, model) + + /** + * Deletes the specified model through the [android.content.ContentResolver]. Uses the deleteUri + * to resolve the reference and the model to [ModelAdapter.getPrimaryConditionClause] + * + * @param contentResolver The content resolver to use + * @param deleteUri A [android.net.Uri] from the [ContentProvider] + * @param model The model to delete + * @return The number of rows deleted. + */ + @JvmStatic + fun delete(contentResolver: ContentResolver, deleteUri: Uri, model: TableClass): Int { + val adapter = model.javaClass.modelAdapter + + val count = contentResolver.delete(deleteUri, adapter.getPrimaryConditionClause(model).query, null) + + // reset autoincrement to 0 + if (count > 0) { + adapter.updateAutoIncrement(model, 0) + } else { + FlowLog.log(FlowLog.Level.W, "A delete on ${model.javaClass} within the ContentResolver appeared to fail.") + } + return count + } + + /** + * Queries the [android.content.ContentResolver] with the specified query uri. It generates + * the correct query and returns a [android.database.Cursor] + * + * @param contentResolver The content resolver to use + * @param queryUri The URI of the query + * @param whereConditions The set of [Operator] to query the content provider. + * @param orderBy The order by clause without the ORDER BY + * @param columns The list of columns to query. + * @return A [android.database.Cursor] + */ + @JvmStatic + fun query(contentResolver: ContentResolver, queryUri: Uri, + whereConditions: OperatorGroup, + orderBy: String?, vararg columns: String?): FlowCursor? = + FlowCursor.from(contentResolver.query(queryUri, columns, whereConditions.query, null, orderBy)) + + /** + * Queries the [android.content.ContentResolver] with the specified queryUri. It will generate + * the correct query and return a list of [TableClass] + * + * @param queryUri The URI of the query + * @param table The table to get from. + * @param whereConditions The set of [Operator] to query the content provider. + * @param orderBy The order by clause without the ORDER BY + * @param columns The list of columns to query. + * @return A list of [TableClass] + */ + @JvmStatic + fun queryList(context: Context, + queryUri: Uri, table: Class, + databaseWrapper: DatabaseWrapper, + whereConditions: OperatorGroup, + orderBy: String, vararg columns: String): List? = + queryList(context.contentResolver, queryUri, table, + databaseWrapper, whereConditions, orderBy, *columns) + + + /** + * Queries the [android.content.ContentResolver] with the specified queryUri. It will generate + * the correct query and return a list of [TableClass] + * + * @param contentResolver The content resolver to use + * @param queryUri The URI of the query + * @param table The table to get from. + * @param whereConditions The set of [Operator] to query the content provider. + * @param orderBy The order by clause without the ORDER BY + * @param columns The list of columns to query. + * @return A list of [TableClass] + */ + @JvmStatic + fun queryList(contentResolver: ContentResolver, queryUri: Uri, + table: Class, + databaseWrapper: DatabaseWrapper, + whereConditions: OperatorGroup, + orderBy: String, vararg columns: String): List? { + val cursor = FlowCursor.from(contentResolver.query(queryUri, columns, whereConditions.query, null, orderBy)) + return table.modelAdapter + .listModelLoader + .load(cursor, databaseWrapper) + } + + /** + * Queries the [android.content.ContentResolver] with the specified queryUri. It will generate + * the correct query and return a the first item from the list of [TableClass] + * + * @param queryUri The URI of the query + * @param table The table to get from + * @param whereConditions The set of [Operator] to query the content provider. + * @param orderBy The order by clause without the ORDER BY + * @param columns The list of columns to query. + * @return The first [TableClass] of the list query from the content provider. + */ + @JvmStatic + fun querySingle(context: Context, + queryUri: Uri, table: Class, + databaseWrapper: DatabaseWrapper, + whereConditions: OperatorGroup, + orderBy: String, vararg columns: String): TableClass? = + querySingle(context.contentResolver, queryUri, table, + databaseWrapper, whereConditions, orderBy, *columns) + + /** + * Queries the [android.content.ContentResolver] with the specified queryUri. It will generate + * the correct query and return a the first item from the list of [TableClass] + * + * @param contentResolver The content resolver to use + * @param queryUri The URI of the query + * @param table The table to get from + * @param whereConditions The set of [Operator] to query the content provider. + * @param orderBy The order by clause without the ORDER BY + * @param columns The list of columns to query. + * @return The first [TableClass] of the list query from the content provider. + */ + @JvmStatic + fun querySingle(contentResolver: ContentResolver, + queryUri: Uri, table: Class, + databaseWrapper: DatabaseWrapper, + whereConditions: OperatorGroup, + orderBy: String, vararg columns: String): TableClass? { + val list = queryList(contentResolver, queryUri, table, + databaseWrapper, whereConditions, orderBy, *columns) + return list?.let { if (list.isNotEmpty()) list[0] else null } + } + +} diff --git a/contentprovider/src/main/kotlin/com/dbflow5/provider/ModelProvider.kt b/contentprovider/src/main/kotlin/com/dbflow5/provider/ModelProvider.kt new file mode 100644 index 000000000..d7b5a1606 --- /dev/null +++ b/contentprovider/src/main/kotlin/com/dbflow5/provider/ModelProvider.kt @@ -0,0 +1,52 @@ +package com.dbflow5.provider + +import android.content.ContentResolver +import android.net.Uri +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.Operator +import com.dbflow5.query.OperatorGroup + +/** + * Description: A base interface for Models that are connected to providers. + */ +interface ModelProvider { + + /** + * @return The [android.net.Uri] that passes to a [android.content.ContentProvider] to delete a Model. + */ + val deleteUri: Uri + + /** + * @return The [android.net.Uri] that passes to a [android.content.ContentProvider] to insert a Model. + */ + val insertUri: Uri + + /** + * @return The [android.net.Uri] that passes to a [android.content.ContentProvider] to update a Model. + */ + val updateUri: Uri + + /** + * @return The [android.net.Uri] that passes to a [android.content.ContentProvider] to query a Model. + */ + val queryUri: Uri + + /** + * Queries the [ContentResolver] of the app based on the passed parameters and + * populates this object with the first row from the returned data. + * + * @param whereOperatorGroup The set of [Operator] to filter the query by. + * @param orderBy The order by without the ORDER BY + * @param columns The list of columns to select. Leave blank for * + */ + fun load(whereOperatorGroup: OperatorGroup, + orderBy: String?, + wrapper: DatabaseWrapper, + vararg columns: String?): T? + + /** + * Queries the [ContentResolver] of the app based on the primary keys of the object and returns a + * new object with the first row from the returned data. + */ + fun load(wrapper: DatabaseWrapper): T? +} diff --git a/contentprovider/src/main/kotlin/com/dbflow5/provider/StubContentProvider.kt b/contentprovider/src/main/kotlin/com/dbflow5/provider/StubContentProvider.kt new file mode 100644 index 000000000..2781a3d04 --- /dev/null +++ b/contentprovider/src/main/kotlin/com/dbflow5/provider/StubContentProvider.kt @@ -0,0 +1,45 @@ +package com.dbflow5.provider + +/** + * Description: + */ + +import android.content.ContentProvider +import android.content.ContentValues +import android.database.Cursor +import android.net.Uri + +/** + * Description: Used as a stub, include this in order to work around Android O changes to [ContentProvider] + */ +class StubContentProvider : ContentProvider() { + + override fun insert(uri: Uri, values: ContentValues?): Uri? { + return null + } + + override fun query(uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String?): Cursor? { + return null + } + + override fun onCreate(): Boolean { + return true + } + + override fun update(uri: Uri, values: ContentValues?, + selection: String?, selectionArgs: Array?): Int { + return 0 + } + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { + return 0 + } + + override fun getType(uri: Uri): String? { + return null + } +} diff --git a/dbflow-sqlcipher/.gitignore b/core/.gitignore similarity index 100% rename from dbflow-sqlcipher/.gitignore rename to core/.gitignore diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 000000000..b244434f6 --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,18 @@ +apply plugin: 'kotlin' + +project.ext.artifactId = bt_name + +targetCompatibility = JavaVersion.VERSION_1_8 +sourceCompatibility = JavaVersion.VERSION_1_8 + +dependencies { + compile deps.kotlin +} + +apply from: '../kotlin-artifacts.gradle' + +compileKotlin { + kotlinOptions { + apiVersion = "1.2" + } +} \ No newline at end of file diff --git a/dbflow-core/gradle.properties b/core/gradle.properties similarity index 100% rename from dbflow-core/gradle.properties rename to core/gradle.properties diff --git a/core/src/main/kotlin/com/dbflow5/StringUtils.kt b/core/src/main/kotlin/com/dbflow5/StringUtils.kt new file mode 100644 index 000000000..923135a60 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/StringUtils.kt @@ -0,0 +1,118 @@ +@file:JvmName("StringUtils") + +package com.dbflow5 + +import com.dbflow5.sql.SQLiteType +import java.util.regex.Pattern + + +/** + * @return true if the string is not null, empty string "", or the length is greater than 0 + */ +fun String?.isNullOrEmpty(): Boolean = + this == null || this == "" || isEmpty() + +/** + * @return true if the string is null, empty string "", or the length is less than equal to 0 + */ +fun String?.isNotNullOrEmpty(): Boolean = + this != null && this != "" && isNotEmpty() + +fun StringBuilder.appendQuotedIfNeeded(string: String?) = apply { + if (string == "*") + return append(string) + + append(string.quoteIfNeeded()) + return this +} + +private val QUOTE = '`' +private val QUOTE_PATTERN: Pattern = (QUOTE + ".*" + QUOTE).toPattern() + +/** + * Helper method to check if name is quoted. + * + * @return true if the name is quoted. We may not want to quote something if its already so. + */ +fun String?.isQuoted(): Boolean = QUOTE_PATTERN.matcher(this).find() + +/** + * @param columnName The column name to use. + * @return A name in quotes. E.G. index => `index` so we can use keywords as column names without fear + * of clashing. + */ +fun String?.quote(): String = "${QUOTE}${this?.replace(".", "`.`")}${QUOTE}" + +fun String?.quoteIfNeeded() = if (this != null && !isQuoted()) { + quote() +} else { + this +} + +/** + * Appends the [SQLiteType] to [StringBuilder] + */ +fun StringBuilder.appendSQLiteType(sqLiteType: SQLiteType): StringBuilder = append(sqLiteType.name) + +/** + * Strips quotes out of a identifier if need to do so. + * + * @param name The name ot strip the quotes from. + * @return A non-quoted name. + */ +fun String?.stripQuotes(): String? { + var ret: String? = this + if (ret != null && ret.isQuoted()) { + ret = ret.replace("`", "") + } + return ret +} + + +/** + * Appends a value only if it's not empty or null + * + * @param name The name of the qualifier + * @param value The value to append after the name + * @return This instance + */ +fun StringBuilder.appendQualifier(name: String?, value: String?): StringBuilder { + if (value != null && value.isNotEmpty()) { + if (name != null) { + append(name) + } + append(" $value ") + } + return this +} + +/** + * Appends the value only if its not null + * + * @param value If not null, its string representation. + * @return This instance + */ +fun StringBuilder.appendOptional(value: Any?): StringBuilder { + if (value != null) { + append(value) + } + return this +} + +/** + * Appends an array of these objects by joining them with a comma with + * [.join] + * + * @param objects The array of objects to pass in + * @return This instance + */ +fun StringBuilder.appendArray(vararg objects: Any): StringBuilder = append(objects.joinToString()) + +/** + * Appends a list of objects by joining them with a comma with + * [.join] + * + * @param objects The list of objects to pass in + * @return This instance + */ +fun StringBuilder.appendList(objects: List<*>): StringBuilder = append(objects.joinToString()) diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Collate.java b/core/src/main/kotlin/com/dbflow5/annotation/Collate.kt similarity index 91% rename from dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Collate.java rename to core/src/main/kotlin/com/dbflow5/annotation/Collate.kt index c55e2ffaa..caed7b309 100644 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Collate.java +++ b/core/src/main/kotlin/com/dbflow5/annotation/Collate.kt @@ -1,9 +1,9 @@ -package com.raizlabs.android.dbflow.annotation; +package com.dbflow5.annotation /** * Represents a SQL Collate method for comparing string columns. */ -public enum Collate { +enum class Collate { /** * Tells the table creation and condition that we wont use collation diff --git a/core/src/main/kotlin/com/dbflow5/annotation/Column.kt b/core/src/main/kotlin/com/dbflow5/annotation/Column.kt new file mode 100644 index 000000000..0309cd53a --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/Column.kt @@ -0,0 +1,47 @@ +package com.dbflow5.annotation + +import com.dbflow5.converter.TypeConverter +import kotlin.reflect.KClass + +/** + * Description: Marks a field as corresponding to a column in the DB. + * When adding new columns or changing names, you need to define a new [Migration]. + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.FIELD) +annotation class Column( + /** + * @return The name of the column. The default is the field name. + */ + val name: String = "", + /** + * @return An optional column length + */ + val length: Int = -1, + /** + * @return Marks the field as having a specified collation to use in it's creation. + */ + val collate: Collate = Collate.NONE, + /** + * @return Adds a default value for this column when saving. This is a string representation + * of the value. + * Note this will place it in when saving + * to the DB because we cannot know the intention of missing data from a query. + */ + val defaultValue: String = "", + /** + * @return If private, by default this is get{Name}() for "name". To define a custom one, this method specifies the name + * of the method only, and not any specific params. So for "getAnotherName()" you would use "AnotherName" as the param. + */ + val getterName: String = "", + /** + * @return If private, by default this is set{Name}() for "name". To define a custom one, this method specifies the name + * of the method only, and not any specific params. So for "setAnotherName(String name)" you would use "AnotherName" as the param. + * The params must align exactly to an expected setter, otherwise a compile error ensues. + */ + val setterName: String = "", + /** + * @return A custom type converter that's only used for this field. It will be created and used in + * the Adapter associated with this table. + */ + val typeConverter: KClass> = TypeConverter::class) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ColumnIgnore.kt b/core/src/main/kotlin/com/dbflow5/annotation/ColumnIgnore.kt new file mode 100644 index 000000000..a75591c9c --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ColumnIgnore.kt @@ -0,0 +1,8 @@ +package com.dbflow5.annotation + +/** + * Description: An annotation used to ignore a column in the [Table.allFields] instance. + */ +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.FIELD) +annotation class ColumnIgnore diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ColumnMap.kt b/core/src/main/kotlin/com/dbflow5/annotation/ColumnMap.kt new file mode 100644 index 000000000..7ca70399e --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ColumnMap.kt @@ -0,0 +1,15 @@ +package com.dbflow5.annotation + +/** + * Description: Maps an arbitrary object and its corresponding fields into a set of columns. It is similar + * to [ForeignKey] except it's not represented in the DB hierarchy. + */ +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.FIELD) +annotation class ColumnMap( + /** + * Defines explicit references for a composite [ColumnMap] definition. + * + * @return override explicit usage of all fields and provide custom references. + */ + val references: Array = []) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ColumnMapReference.kt b/core/src/main/kotlin/com/dbflow5/annotation/ColumnMapReference.kt new file mode 100644 index 000000000..4d0a2aa76 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ColumnMapReference.kt @@ -0,0 +1,35 @@ +package com.dbflow5.annotation + +import com.dbflow5.converter.TypeConverter +import kotlin.reflect.KClass + +/** + * Description: Allows a [ColumnMap] to specify a reference override for its fields. Anything not + * defined here will not be used. + */ +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.FIELD) +annotation class ColumnMapReference( + /** + * @return The local column name that will be referenced in the DB + */ + val columnName: String, + /** + * @return The column name in the referenced table + */ + val columnMapFieldName: String, + /** + * @return The default value for the reference column. Same as [Column.defaultValue] + */ + val defaultValue: String = "", + + /** + * @return A custom type converter that's only used for this field. It will be created and used in + * the Adapter associated with this table. + */ + val typeConverter: KClass> = TypeConverter::class, + + /** + * @return Specify the [NotNull] annotation here and it will get pasted into the reference definition. + */ + val notNull: NotNull = NotNull(onNullConflict = ConflictAction.NONE)) diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ConflictAction.java b/core/src/main/kotlin/com/dbflow5/annotation/ConflictAction.kt similarity index 83% rename from dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ConflictAction.java rename to core/src/main/kotlin/com/dbflow5/annotation/ConflictAction.kt index 2d4e1e6ee..b6a75a3b4 100644 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ConflictAction.java +++ b/core/src/main/kotlin/com/dbflow5/annotation/ConflictAction.kt @@ -1,10 +1,10 @@ -package com.raizlabs.android.dbflow.annotation; +package com.dbflow5.annotation /** - * This is how to resolve null or unique conflicts with a field marked as {@link NotNull} - * or {@link Unique} + * This is how to resolve null or unique conflicts with a field marked as [NotNull] + * or [Unique] */ -public enum ConflictAction { +enum class ConflictAction { /** * Do nothing and do not specify any algorithm. @@ -15,7 +15,7 @@ public enum ConflictAction { * When an applicable constraint violation occurs, the ROLLBACK resolution algorithm aborts the * current SQL statement with an SQLITE_CONSTRAINT error and rolls back the current transaction. * If no transaction is active (other than the implied transaction that is created on every command) - * then the ROLLBACK resolution algorithm works the same as the {@link #ABORT} algorithm. + * then the ROLLBACK resolution algorithm works the same as the [.ABORT] algorithm. */ ROLLBACK, @@ -60,20 +60,18 @@ public enum ConflictAction { */ REPLACE; - public static int getSQLiteDatabaseAlgorithmInt(ConflictAction conflictAction) { - switch (conflictAction) { - case ROLLBACK: - return 1; - case ABORT: - return 2; - case FAIL: - return 3; - case IGNORE: - return 4; - case REPLACE: - return 5; - default: - return 0; - } + + companion object { + + @JvmStatic + fun getSQLiteDatabaseAlgorithmInt(conflictAction: ConflictAction) = + when (conflictAction) { + ROLLBACK -> 1 + ABORT -> 2 + FAIL -> 3 + IGNORE -> 4 + REPLACE -> 5 + else -> 0 + } } } diff --git a/core/src/main/kotlin/com/dbflow5/annotation/Database.kt b/core/src/main/kotlin/com/dbflow5/annotation/Database.kt new file mode 100644 index 000000000..ce210cfce --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/Database.kt @@ -0,0 +1,52 @@ +package com.dbflow5.annotation + +/** + * Description: Creates a new database to use in the application. + * + * + * If we specify one DB, then all models do not need to specify a DB. As soon as we specify two, then each + * model needs to define what DB it points to. + * + * + * + * Models will specify which DB it belongs to, + * but they currently can only belong to one DB. + * + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +@Retention(AnnotationRetention.SOURCE) +annotation class Database( + /** + * @return The current version of the DB. Increment it to trigger a DB update. + */ + val version: Int, + @Deprecated("use DatabaseConfig.databaseName() to change the name.") + val name: String = "", + @Deprecated("use DatabaseConfig.extension() to change the extension.") + val databaseExtension: String = "", + @Deprecated("use DatabaseConfig.inMemoryBuilder() instead.") + val inMemory: Boolean = false, + /** + * @return If true, SQLite will throw exceptions when [ForeignKey] constraints are not respected. + * Default is false and will not throw exceptions. + */ + val foreignKeyConstraintsEnforced: Boolean = false, + /** + * @return Checks for consistency in the DB, if true it will recopy over the prepackage database. + */ + val consistencyCheckEnabled: Boolean = false, + /** + * @return Keeps a backup for whenever the database integrity fails a "PRAGMA quick_check(1)" that will + * replace the corrupted DB + */ + val backupEnabled: Boolean = false, + /** + * @return Global default insert conflict that can be applied to any table when it leaves + * its [ConflictAction] as NONE. + */ + val insertConflict: ConflictAction = ConflictAction.NONE, + /** + * @return Global update conflict that can be applied to any table when it leaves its + * [ConflictAction] as NONE + */ + val updateConflict: ConflictAction = ConflictAction.NONE) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ForeignKey.kt b/core/src/main/kotlin/com/dbflow5/annotation/ForeignKey.kt new file mode 100644 index 000000000..ebd035380 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ForeignKey.kt @@ -0,0 +1,64 @@ +package com.dbflow5.annotation + +import kotlin.reflect.KClass + +/** + * Description: + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.FIELD) +annotation class ForeignKey( + /** + * Defines explicit references for a composite [ForeignKey] definition. This is no longer required + * as the library will auto-generate references for you based on the other table's primary keys. + * + * @return the set of explicit references if you wish to have different values than default generated. + */ + val references: Array = [], + /** + * @return Default false. When this column is a [ForeignKey] and table object, + * returning true will save the model before adding the fields to save as a foreign key. + * If false, we expect the field to not change and must save the model manually outside + * of the ModelAdapter before saving the child class. + */ + val saveForeignKeyModel: Boolean = false, + /** + * @return Default false. When this column is a [ForeignKey] and table object, + * returning true will delte the model before deleting its enclosing child class. + * If false, we expect the field to not change and must delete the model manually outside + * of the ModelAdapter before saving the child class. + */ + val deleteForeignKeyModel: Boolean = false, + /** + * @return Replaces legacy ForeignKeyContainer, this method instructs the code generator to only + * populate the model with the [ForeignKeyReference] defined in this field. This skips + * the Select retrieval convenience. + */ + val stubbedRelationship: Boolean = false, + /** + * @return If true, during a transaction, FK constraints are not violated immediately until the resulting transaction commits. + * This is useful for out of order foreign key operations. + * @see [Deferred Foreign Key Constraints](http://www.sqlite.org/foreignkeys.html.fk_deferred) + */ + val deferred: Boolean = false, + /** + * @return an optional table class that this reference points to. It's only used if the field + * is NOT a Model class. + */ + val tableClass: KClass<*> = Any::class, + /** + * Defines [ForeignKeyAction] action to be performed + * on delete of referenced record. Defaults to [ForeignKeyAction.NO_ACTION]. Used only when + * columnType is [ForeignKey]. + * + * @return [ForeignKeyAction] + */ + val onDelete: ForeignKeyAction = ForeignKeyAction.NO_ACTION, + /** + * Defines [ForeignKeyAction] action to be performed + * on update of referenced record. Defaults to [ForeignKeyAction.NO_ACTION]. Used only when + * columnType is [ForeignKey]. + * + * @return [ForeignKeyAction] + */ + val onUpdate: ForeignKeyAction = ForeignKeyAction.NO_ACTION) diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKeyAction.java b/core/src/main/kotlin/com/dbflow5/annotation/ForeignKeyAction.kt similarity index 85% rename from dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKeyAction.java rename to core/src/main/kotlin/com/dbflow5/annotation/ForeignKeyAction.kt index b982d3930..89d227916 100644 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKeyAction.java +++ b/core/src/main/kotlin/com/dbflow5/annotation/ForeignKeyAction.kt @@ -1,9 +1,9 @@ -package com.raizlabs.android.dbflow.annotation; +package com.dbflow5.annotation /** * Actions associated with on update and on delete */ -public enum ForeignKeyAction { +enum class ForeignKeyAction { /** * When a parent key is modified or deleted from the database, no special action is taken */ @@ -20,7 +20,7 @@ public enum ForeignKeyAction { */ SET_NULL, /** - * The "SET DEFAULT" actions are similar to {@link ForeignKeyAction#SET_NULL}, except that each of the child key + * The "SET DEFAULT" actions are similar to [ForeignKeyAction.SET_NULL], except that each of the child key * columns is set to contain the columns default value instead of NULL */ SET_DEFAULT, @@ -30,5 +30,5 @@ public enum ForeignKeyAction { * the deleted parent row is also deleted. For an "ON UPDATE CASCADE" action, it means that the values * stored in each dependent child key are modified to match the new parent key values. */ - CASCADE, + CASCADE } diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ForeignKeyReference.kt b/core/src/main/kotlin/com/dbflow5/annotation/ForeignKeyReference.kt new file mode 100644 index 000000000..0430a075c --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ForeignKeyReference.kt @@ -0,0 +1,30 @@ +package com.dbflow5.annotation + +/** + * Description: Used inside of [ForeignKey.references], describes the + * local column name, type, and referencing table column name. + * + * + * Note: the type of the local column must match the + * column type of the referenced column. By using a field as a Model object, + * you will need to ensure the same types are used. + */ +@Retention(AnnotationRetention.SOURCE) +annotation class ForeignKeyReference( + /** + * @return The local column name that will be referenced in the DB + */ + val columnName: String, + /** + * @return The column name in the referenced table + */ + val foreignKeyColumnName: String, + /** + * @return The default value for the reference column. Same as [Column.defaultValue] + */ + val defaultValue: String = "", + + /** + * @return Specify the [NotNull] annotation here and it will get pasted into the reference definition. + */ + val notNull: NotNull = NotNull(onNullConflict = ConflictAction.NONE)) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/Index.kt b/core/src/main/kotlin/com/dbflow5/annotation/Index.kt new file mode 100644 index 000000000..1838a9af3 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/Index.kt @@ -0,0 +1,13 @@ +package com.dbflow5.annotation + +/** + * Description: Creates an index for a specified [Column]. A single column can belong to multiple + * indexes within the same table if you wish. + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.FIELD) +annotation class Index( + /** + * @return The set of index groups that this index belongs to. + */ + val indexGroups: IntArray = []) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/IndexGroup.kt b/core/src/main/kotlin/com/dbflow5/annotation/IndexGroup.kt new file mode 100644 index 000000000..172cd9e72 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/IndexGroup.kt @@ -0,0 +1,24 @@ +package com.dbflow5.annotation + +const val INDEX_GENERIC = -1 + +/** + * Description: + */ +@Target(AnnotationTarget.ANNOTATION_CLASS) +@Retention(AnnotationRetention.SOURCE) +annotation class IndexGroup( + /** + * @return The number that each contained [Index] points to, so they can be combined into a single index. + * If [.INDEX_GENERIC], this will assume a generic index that covers the whole table. + */ + val number: Int = INDEX_GENERIC, + /** + * @return The name of this index. It must be unique from other [IndexGroup]. + */ + val name: String, + /** + * @return If true, this will disallow duplicate values to be inserted into the table. + */ + val unique: Boolean = false) + diff --git a/core/src/main/kotlin/com/dbflow5/annotation/InheritedColumn.kt b/core/src/main/kotlin/com/dbflow5/annotation/InheritedColumn.kt new file mode 100644 index 000000000..1c44bd4d1 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/InheritedColumn.kt @@ -0,0 +1,23 @@ +package com.dbflow5.annotation + +import com.dbflow5.annotation.ConflictAction + +/** + * Description: Allows [Table] to inherit fields from other objects to make it part of the DB table. + */ +@Target(AnnotationTarget.ANNOTATION_CLASS) +@Retention(AnnotationRetention.SOURCE) +annotation class InheritedColumn( + /** + * @return The column annotation as if it was part of the class + */ + val column: Column, + /** + * @return The field name that an inherited column uses. It must match exactly case-by-case to the field you're referencing. + * If the field is private, the [Column] allows you to define getter and setters for it. + */ + val fieldName: String, + /** + * @return If specified other than [ConflictAction.NONE], then we assume [NotNull]. + */ + val nonNullConflict: ConflictAction = ConflictAction.NONE) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/InheritedPrimaryKey.kt b/core/src/main/kotlin/com/dbflow5/annotation/InheritedPrimaryKey.kt new file mode 100644 index 000000000..ba4f9b421 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/InheritedPrimaryKey.kt @@ -0,0 +1,21 @@ +package com.dbflow5.annotation + +/** + * Description: Allows you to specify a non-Column to be inherited and used as a [PrimaryKey] + */ +@Target(AnnotationTarget.ANNOTATION_CLASS) +@Retention(AnnotationRetention.SOURCE) +annotation class InheritedPrimaryKey( + /** + * @return The primary key annotation as if it was part of the class + */ + val primaryKey: PrimaryKey, + /** + * @return The column annotation as if it was part of the class + */ + val column: Column, + /** + * @return The field name that an inherited column uses. It must match exactly case-by-case to the field you're referencing. + * If the field is private, the [PrimaryKey] allows you to define getter and setters for it. + */ + val fieldName: String) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ManyToMany.kt b/core/src/main/kotlin/com/dbflow5/annotation/ManyToMany.kt new file mode 100644 index 000000000..21d07fd89 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ManyToMany.kt @@ -0,0 +1,42 @@ +package com.dbflow5.annotation + +import kotlin.reflect.KClass + +/** + * Description: Builds a many-to-many relationship with another [Table]. Only one table needs to specify + * the annotation and its assumed that they use primary keys only. The generated + * class will contain an auto-incrementing primary key by default. + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class ManyToMany( + /** + * @return The other table class by which this will get merged. + */ + val referencedTable: KClass<*>, + /** + * @return A name that we use as the column name for the referenced table in the + * generated ManyToMany table class. + */ + val referencedTableColumnName: String = "", + /** + * @return A name that we use as the column name for this specific table's name. + */ + val thisTableColumnName: String = "", + /** + * @return By default, we generate an auto-incrementing [Long] [PrimaryKey]. + * If false, all [PrimaryKey] of the corresponding tables will be placed as [ForeignKey] and [PrimaryKey] + * of the generated table instead of using an autoincrementing Long [PrimaryKey]. + */ + val generateAutoIncrement: Boolean = true, + /** + * @return by default, we append {selfTable}{generatedClassSeparator}{referencedTable} or "User_Follower", + * for example. If you want different name, change this. + */ + val generatedTableClassName: String = "", + /** + * @return by default the Models referenced here are not saved prior to saving this + * object for obvious efficiency reasons. + * @see ForeignKey.saveForeignKeyModel + */ + val saveForeignKeyModels: Boolean = false) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/Migration.kt b/core/src/main/kotlin/com/dbflow5/annotation/Migration.kt new file mode 100644 index 000000000..75de88f25 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/Migration.kt @@ -0,0 +1,25 @@ +package com.dbflow5.annotation + +import kotlin.reflect.KClass + +/** + * Description: Marks a Migration class to be included in DB construction. The class using this annotation + * must implement the Migration interface. + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class Migration( + /** + * @return The version the migration will trigger at. + */ + val version: Int, + /** + * @return Specify the database class that this migration belongs to. + */ + val database: KClass<*>, + /** + * @return If number greater than -1, the migrations from the same [.version] get ordered from + * highest to lowest. if they are the same priority, there is no telling which one is executed first. The + * annotation processor will process in order it finds the classes. + */ + val priority: Int = -1) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ModelCacheField.kt b/core/src/main/kotlin/com/dbflow5/annotation/ModelCacheField.kt new file mode 100644 index 000000000..e327ce8ca --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ModelCacheField.kt @@ -0,0 +1,8 @@ +package com.dbflow5.annotation + +/** + * Description: marks a single field as a ModelCache creator that is used in the corresponding ModelAdapter. + */ +@Target(AnnotationTarget.FIELD) +@Retention(AnnotationRetention.SOURCE) +annotation class ModelCacheField diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ModelView.kt b/core/src/main/kotlin/com/dbflow5/annotation/ModelView.kt new file mode 100644 index 000000000..a7744454a --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ModelView.kt @@ -0,0 +1,32 @@ +package com.dbflow5.annotation + +import com.dbflow5.sql.Query +import kotlin.reflect.KClass + +/** + * Author: andrewgrosner + * Description: Marks a class as being an SQL VIEW definition. It must extend BaseModelView and have + * a single public, static, final field that is annotated with [ModelViewQuery] and be a [Query]. + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class ModelView( + /** + * @return The name of this view. Default is the class name. + */ + val name: String = "", + /** + * @return The class of the database this corresponds to. + */ + val database: KClass<*>, + /** + * @return When true, all public, package-private , non-static, and non-final fields of the reference class are considered as [com.dbflow5.annotation.Column] . + * The only required annotated field becomes The [PrimaryKey] + * or [PrimaryKey.autoincrement]. + */ + val allFields: Boolean = true, + /** + * @return The higher the number, the order by which the creation of this class gets called. + * Useful for creating ones that depend on another [ModelView]. + */ + val priority: Int = 0) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/ModelViewQuery.kt b/core/src/main/kotlin/com/dbflow5/annotation/ModelViewQuery.kt new file mode 100644 index 000000000..a7419c2fa --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/ModelViewQuery.kt @@ -0,0 +1,12 @@ +package com.dbflow5.annotation + +import com.dbflow5.sql.Query + +/** + * Description: Represents a field that is a [Query]. This is only meant to be used as a query + * reference in [ModelView]. This is so the annotation processor knows how to access the query of + * the view. + */ +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER) +@Retention(AnnotationRetention.SOURCE) +annotation class ModelViewQuery diff --git a/core/src/main/kotlin/com/dbflow5/annotation/MultiCacheField.kt b/core/src/main/kotlin/com/dbflow5/annotation/MultiCacheField.kt new file mode 100644 index 000000000..f025b2c56 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/MultiCacheField.kt @@ -0,0 +1,9 @@ +package com.dbflow5.annotation + +/** + * Description: Marks a field as the IMultiKeyCacheModel that we use to convert multiple fields into + * a single key for caching. + */ +@Target(AnnotationTarget.FIELD) +@Retention(AnnotationRetention.SOURCE) +annotation class MultiCacheField diff --git a/core/src/main/kotlin/com/dbflow5/annotation/MultipleManyToMany.kt b/core/src/main/kotlin/com/dbflow5/annotation/MultipleManyToMany.kt new file mode 100644 index 000000000..659c32c2c --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/MultipleManyToMany.kt @@ -0,0 +1,8 @@ +package com.dbflow5.annotation + +/** + * Description: Provides ability to add multiple [ManyToMany] annotations at once. + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class MultipleManyToMany(vararg val value: ManyToMany) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/NotNull.kt b/core/src/main/kotlin/com/dbflow5/annotation/NotNull.kt new file mode 100644 index 000000000..b94e9eb75 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/NotNull.kt @@ -0,0 +1,16 @@ +package com.dbflow5.annotation + +import com.dbflow5.annotation.ConflictAction + +/** + * Description: Specifies that a [Column] is not null. + */ +@Target(AnnotationTarget.FIELD) +@Retention(AnnotationRetention.SOURCE) +annotation class NotNull( + /** + * Defines how to handle conflicts for not null column + * + * @return a [com.dbflow5.annotation.ConflictAction] enum + */ + val onNullConflict: ConflictAction = ConflictAction.FAIL) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/OneToMany.kt b/core/src/main/kotlin/com/dbflow5/annotation/OneToMany.kt new file mode 100644 index 000000000..1720d22b0 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/OneToMany.kt @@ -0,0 +1,51 @@ +package com.dbflow5.annotation + +/** + * Description: Describes a 1-many relationship. It applies to some method that returns a [List] of Model objects. + * This annotation can handle loading, deleting, and saving when the current data changes. By default it will call the + * associated method when the containing class operates. + */ +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Retention(AnnotationRetention.SOURCE) +annotation class OneToMany( + /** + * @return The methods you wish to call it from. By default it's loaded out of the DB. + */ + val oneToManyMethods: Array = [(OneToManyMethod.LOAD)], + /** + * @return The name of the list variable to use. If is left blank, we will remove the "get" and then decapitalize the remaining name. + */ + val variableName: String = "", + /** + * @return If true, the code generated for this relationship done as efficiently as possible. + * It will not work on nested relationships, caching, and other code that requires overriding of BaseModel or Model operations. + */ + val efficientMethods: Boolean = true) + +/** + * The method to apply the OneToMany to. + */ +enum class OneToManyMethod { + + /** + * Load this relationship when the parent model loads from the database. This is called before the OnLoadFromCursor + * method, but after other columns load. + */ + LOAD, + + /** + * Inserts code to delete the results returned from the List relationship when the parent model is deleted. + */ + DELETE, + + /** + * Inserts code to save the list of models when the parent model is saved. + */ + SAVE, + + /** + * Shorthand to support all options. + */ + ALL +} + diff --git a/core/src/main/kotlin/com/dbflow5/annotation/PrimaryKey.kt b/core/src/main/kotlin/com/dbflow5/annotation/PrimaryKey.kt new file mode 100644 index 000000000..103a7cace --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/PrimaryKey.kt @@ -0,0 +1,22 @@ +package com.dbflow5.annotation + +/** + * Description: + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.FIELD) +annotation class PrimaryKey( + /** + * Specifies if the column is autoincrementing or not + */ + val autoincrement: Boolean = false, + /** + * Specifies the column to be treated as a ROWID but is not an [.autoincrement]. This + * overrides [.autoincrement] and is mutually exclusive. + */ + val rowID: Boolean = false, + /** + * @return When true, we simple do {columnName} > 0 when checking for it's existence if [.autoincrement] + * is true. If not, we do a full database SELECT exists. + */ + val quickCheckAutoIncrement: Boolean = false) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/QueryModel.kt b/core/src/main/kotlin/com/dbflow5/annotation/QueryModel.kt new file mode 100644 index 000000000..a3d65df3d --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/QueryModel.kt @@ -0,0 +1,20 @@ +package com.dbflow5.annotation + +import kotlin.reflect.KClass + +/** + * Description: Marks a Model class as NOT a [Table], but generates code for retrieving data from a + * generic query + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class QueryModel( + /** + * @return Specify the class of the database to use. + */ + val database: KClass<*>, + /** + * @return If true, all accessible, non-static, and non-final fields are treated as valid fields. + * @see Table.allFields + */ + val allFields: Boolean = true) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/Table.kt b/core/src/main/kotlin/com/dbflow5/annotation/Table.kt new file mode 100644 index 000000000..ec018773d --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/Table.kt @@ -0,0 +1,96 @@ +package com.dbflow5.annotation + +import com.dbflow5.annotation.ConflictAction +import kotlin.reflect.KClass + +val DEFAULT_CACHE_SIZE = 25 + +/** + * Author: andrewgrosner + * Description: Marks a class as being a table for only ONE DB. It must implement the Model interface and all fields MUST be package private. + * This will generate a $Table and $Adapter class. The $Table class generates static final column name variables to reference in queries. + * The $Adapter class defines how to retrieve and store this object as well as other methods for acting on model objects in the database. + */ +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class Table( + /** + * @return Specifies a different name for the table than the name of the Model class. + */ + val name: String = "", + /** + * @return Specify the database class that this table belongs to. It must have the [Database] annotation. + */ + val database: KClass<*>, + /** + * @return Specify the general conflict algorithm used by this table when updating records. + */ + val updateConflict: com.dbflow5.annotation.ConflictAction = com.dbflow5.annotation.ConflictAction.NONE, + /** + * @return Specify the general insert conflict algorithm used by this table. + */ + val insertConflict: com.dbflow5.annotation.ConflictAction = com.dbflow5.annotation.ConflictAction.NONE, + /** + * @return An optional [ConflictAction] that we append to creation for conflict handling in PK. + */ + val primaryKeyConflict: com.dbflow5.annotation.ConflictAction = com.dbflow5.annotation.ConflictAction.NONE, + /** + * @return When true, all public, package-private , non-static, and non-final fields of the reference class are considered as [com.dbflow5.annotation.Column] . + * The only required annotated field becomes The [PrimaryKey] + * or [PrimaryKey.autoincrement]. + */ + val allFields: Boolean = true, + /** + * @return If true, all private boolean fields will use "is" instead of "get" for its getter and + * "set" without the "is" if it starts with "is" + */ + val useBooleanGetterSetters: Boolean = true, + /** + * @return If true, caching mechanism is enabled. This works for single primary key tables. For + * multi-primary key tables, IMultiKeyCacheModel interface is required to specify the caching key. + */ + val cachingEnabled: Boolean = false, + /** + * @return If true, we throw away checks for column indexing and simply assume that the cursor returns + * all our columns in order. This may provide a slight performance boost. + */ + val orderedCursorLookUp: Boolean = false, + /** + * @return When true, we reassign the corresponding Model's fields to default values when loading + * from cursor. If false, we assign values only if present in Cursor. + */ + val assignDefaultValuesFromCursor: Boolean = true, + /** + * @return When false, this table gets generated and associated with database, however it will not immediately + * get created upon startup. This is useful for keeping around legacy tables for migrations. + */ + val createWithDatabase: Boolean = true, + + /** + * If true, generates ContentValues bindings for a table. By default it no longer does. + */ + val generateContentValues: Boolean = false, + /** + * @return The cache size for this Table. + */ + val cacheSize: Int = 25, + /** + * @return Declares a set of UNIQUE columns with the corresponding [ConflictAction]. A [Column] + * will point to this group using [Unique.uniqueGroups] + */ + val uniqueColumnGroups: Array = [], + /** + * @return The set of INDEX clauses that specific columns can define to belong to, using the [Index] annotation. + * The generated Index properties belong to the corresponding property class to this table. + */ + val indexGroups: Array = [], + /** + * @return A set of inherited accessible fields not necessarily defined as columns in the super class of this table. + * Each must be accessible via: public, package private, or protected or getter/setters. + */ + val inheritedColumns: Array = [], + /** + * @return A set of inherited accessible fields not necessarily defined as columns in the super class of this table. + * Each must be accessible via: public, package private, or protected or getter/setters. + */ + val inheritedPrimaryKeys: Array = []) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/TypeConverter.kt b/core/src/main/kotlin/com/dbflow5/annotation/TypeConverter.kt new file mode 100644 index 000000000..6e863a591 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/TypeConverter.kt @@ -0,0 +1,17 @@ +package com.dbflow5.annotation + +import kotlin.reflect.KClass + +/** + * Author: andrewgrosner + * Description: Marks a class as being a TypeConverter. A type converter will turn a non-model, non-SQLiteTyped class into + * a valid database type. + */ +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class TypeConverter( + /** + * @return Specify a set of subclasses by which the [TypeConverter] registers for. For + * each one, this will create a new instance of the converter. + */ + val allowedSubtypes: Array> = []) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/Unique.kt b/core/src/main/kotlin/com/dbflow5/annotation/Unique.kt new file mode 100644 index 000000000..e0c9b456a --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/Unique.kt @@ -0,0 +1,24 @@ +package com.dbflow5.annotation + +/** + * Description: Marks the field as unique, meaning its value cannot be repeated. It is, however, + * NOT a primary key. + */ +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.FIELD) +annotation class Unique( + /** + * @return if field is unique. If false, we expect [.uniqueGroups] to be specified.` + */ + val unique: Boolean = true, + /** + * @return Marks a unique field as part of a unique group. For every unique number entered, + * it will generate a UNIQUE() column statement. + */ + val uniqueGroups: IntArray = [], + /** + * Defines how to handle conflicts for a unique column + * + * @return a [ConflictAction] enum + */ + val onUniqueConflict: ConflictAction = ConflictAction.FAIL) diff --git a/core/src/main/kotlin/com/dbflow5/annotation/UniqueGroup.kt b/core/src/main/kotlin/com/dbflow5/annotation/UniqueGroup.kt new file mode 100644 index 000000000..4667558cb --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/annotation/UniqueGroup.kt @@ -0,0 +1,16 @@ +package com.dbflow5.annotation + +/** + * Description: + */ +@Target(AnnotationTarget.ANNOTATION_CLASS) +@Retention(AnnotationRetention.SOURCE) +annotation class UniqueGroup( + /** + * @return The number that columns point to to use this group + */ + val groupNumber: Int, + /** + * @return The conflict action that this group takes. + */ + val uniqueConflict: ConflictAction = ConflictAction.FAIL) diff --git a/core/src/main/kotlin/com/dbflow5/converter/TypeConverters.kt b/core/src/main/kotlin/com/dbflow5/converter/TypeConverters.kt new file mode 100644 index 000000000..9fb853fac --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/converter/TypeConverters.kt @@ -0,0 +1,119 @@ +package com.dbflow5.converter + +import java.math.BigDecimal +import java.math.BigInteger +import java.sql.Time +import java.sql.Timestamp +import java.util.* + +/** + * Author: andrewgrosner + * Description: This class is responsible for converting the stored database value into the field value in + * a Model. + */ +@com.dbflow5.annotation.TypeConverter +abstract class TypeConverter { + + /** + * Converts the ModelClass into a DataClass + * + * @param model this will be called upon syncing + * @return The DataClass value that converts into a SQLite type + */ + abstract fun getDBValue(model: ModelClass?): DataClass? + + /** + * Converts a DataClass from the DB into a ModelClass + * + * @param data This will be called when the model is loaded from the DB + * @return The ModelClass value that gets set in a Model that holds the data class. + */ + abstract fun getModelValue(data: DataClass?): ModelClass? +} + +/** + * Description: Defines how we store and retrieve a [java.math.BigDecimal] + */ +class BigDecimalConverter : TypeConverter() { + override fun getDBValue(model: BigDecimal?): String? = model?.toString() + + override fun getModelValue(data: String?): BigDecimal? = if (data == null) null else BigDecimal(data) +} + +/** + * Description: Defines how we store and retrieve a [java.math.BigInteger] + */ +class BigIntegerConverter : TypeConverter() { + override fun getDBValue(model: BigInteger?): String? = model?.toString() + + override fun getModelValue(data: String?): BigInteger? = if (data == null) null else BigInteger(data) +} + +/** + * Description: Converts a boolean object into an Integer for database storage. + */ +class BooleanConverter : TypeConverter() { + override fun getDBValue(model: Boolean?): Int? = if (model == null) null else if (model) 1 else 0 + + override fun getModelValue(data: Int?): Boolean? = if (data == null) null else data == 1 +} + +/** + * Description: Defines how we store and retrieve a [java.util.Calendar] + */ +@com.dbflow5.annotation.TypeConverter(allowedSubtypes = [(GregorianCalendar::class)]) +class CalendarConverter : TypeConverter() { + + override fun getDBValue(model: Calendar?): Long? = model?.timeInMillis + + override fun getModelValue(data: Long?): Calendar? = + if (data != null) Calendar.getInstance().apply { timeInMillis = data } else null +} + +/** + * Description: Converts a [Character] into a [String] for database storage. + */ +class CharConverter : TypeConverter() { + + override fun getDBValue(model: Char?): String? = + if (model != null) String(charArrayOf(model)) else null + + override fun getModelValue(data: String?): Char? = if (data != null) data[0] else null +} + +/** + * Description: Defines how we store and retrieve a [java.util.Date] + */ +class DateConverter : TypeConverter() { + + override fun getDBValue(model: Date?): Long? = model?.time + + override fun getModelValue(data: Long?): Date? = if (data == null) null else Date(data) +} + +/** + * Description: Defines how we store and retrieve a [java.sql.Date] + */ +@com.dbflow5.annotation.TypeConverter(allowedSubtypes = [(Time::class), (Timestamp::class)]) +class SqlDateConverter : TypeConverter() { + + override fun getDBValue(model: java.sql.Date?): Long? = model?.time + + override fun getModelValue(data: Long?): java.sql.Date? = if (data == null) null else java.sql.Date(data) +} + +/** + * Description: Responsible for converting a [UUID] to a [String]. + * + * @author Andrew Grosner (fuzz) + */ +class UUIDConverter : TypeConverter() { + + override fun getDBValue(model: UUID?): String? = model?.toString() + + override fun getModelValue(data: String?): UUID? { + return if (data == null) { + null + } else UUID.fromString(data) + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/dbflow5/data/Blob.kt b/core/src/main/kotlin/com/dbflow5/data/Blob.kt new file mode 100644 index 000000000..9e668070f --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/data/Blob.kt @@ -0,0 +1,12 @@ +package com.dbflow5.data + +/** + * Description: Provides a way to support blob format data. + */ +class Blob @JvmOverloads constructor( + /** + * Sets the underlying blob data. + * + * @param blob The set of bytes to use. + */ + var blob: ByteArray? = null) diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/Query.java b/core/src/main/kotlin/com/dbflow5/sql/Query.kt similarity index 62% rename from dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/Query.java rename to core/src/main/kotlin/com/dbflow5/sql/Query.kt index 93c0f0dc1..50b29b12f 100644 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/Query.java +++ b/core/src/main/kotlin/com/dbflow5/sql/Query.kt @@ -1,12 +1,12 @@ -package com.raizlabs.android.dbflow.sql; +package com.dbflow5.sql /** * Description: The basic interface for something that has a piece of a query. */ -public interface Query { +interface Query { /** * @return the SQL query statement here */ - String getQuery(); + val query: String } diff --git a/core/src/main/kotlin/com/dbflow5/sql/QueryCloneable.kt b/core/src/main/kotlin/com/dbflow5/sql/QueryCloneable.kt new file mode 100644 index 000000000..a137de4eb --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/sql/QueryCloneable.kt @@ -0,0 +1,9 @@ +package com.dbflow5.sql + +/** + * Description: + */ +interface QueryCloneable { + + fun cloneSelf(): T +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/dbflow5/sql/SQLiteType.kt b/core/src/main/kotlin/com/dbflow5/sql/SQLiteType.kt new file mode 100644 index 000000000..2b4b90d81 --- /dev/null +++ b/core/src/main/kotlin/com/dbflow5/sql/SQLiteType.kt @@ -0,0 +1,67 @@ +package com.dbflow5.sql + +import com.dbflow5.data.Blob + +/** + * Description: Represents a type that SQLite understands. + */ +enum class SQLiteType { + + /** + * Represents an integer number in the DB. + */ + INTEGER, + + /** + * Represents a floating-point, precise number. + */ + REAL, + + /** + * Represents text. + */ + TEXT, + + /** + * A column defined by [byte[]] data. Usually reserved for images or larger pieces of content. + */ + BLOB; + + + companion object { + + private val sTypeMap = hashMapOf( + Byte::class.javaPrimitiveType!!.name to INTEGER, + Short::class.javaPrimitiveType!!.name to INTEGER, + Int::class.javaPrimitiveType!!.name to INTEGER, + Long::class.javaPrimitiveType!!.name to INTEGER, + Float::class.javaPrimitiveType!!.name to REAL, + Double::class.javaPrimitiveType!!.name to REAL, + Boolean::class.javaPrimitiveType!!.name to INTEGER, + Char::class.javaPrimitiveType!!.name to TEXT, + ByteArray::class.java.name to BLOB, + Byte::class.java.name to INTEGER, + Short::class.java.name to INTEGER, + Int::class.java.name to INTEGER, + Long::class.java.name to INTEGER, + Float::class.java.name to REAL, + Double::class.java.name to REAL, + Boolean::class.java.name to INTEGER, + Char::class.java.name to TEXT, + CharSequence::class.java.name to TEXT, + String::class.java.name to TEXT, + Array::class.java.name to BLOB, + Blob::class.java.name to BLOB) + + /** + * Returns the [SQLiteType] for this class + * + * @param className The fully qualified class name + * @return The type from the class name + */ + + operator fun get(className: String): SQLiteType? = sTypeMap[className] + + fun containsClass(className: String): Boolean = sTypeMap.containsKey(className) + } +} diff --git a/dbflow-tests/.gitignore b/coroutines/.gitignore similarity index 100% rename from dbflow-tests/.gitignore rename to coroutines/.gitignore diff --git a/coroutines/build.gradle b/coroutines/build.gradle new file mode 100644 index 000000000..cccb6b8f6 --- /dev/null +++ b/coroutines/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +project.ext.artifactId = bt_name + +android { + compileSdkVersion target_sdk + defaultConfig { + minSdkVersion 15 + targetSdkVersion target_sdk + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } +} + +dependencies { + implementation project(':lib') + api deps.coroutines +} + +kotlin { + experimental { + coroutines 'enable' + } +} + diff --git a/coroutines/gradle.properties b/coroutines/gradle.properties new file mode 100644 index 000000000..b4da7dfc7 --- /dev/null +++ b/coroutines/gradle.properties @@ -0,0 +1,3 @@ +bt_name=dbflow-coroutines +bt_packaging=aar +bt_artifact_id=dbflow-coroutines \ No newline at end of file diff --git a/coroutines/proguard-rules.pro b/coroutines/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/coroutines/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/dbflow-sqlcipher/settings.gradle b/coroutines/settings.gradle similarity index 100% rename from dbflow-sqlcipher/settings.gradle rename to coroutines/settings.gradle diff --git a/coroutines/src/main/AndroidManifest.xml b/coroutines/src/main/AndroidManifest.xml new file mode 100644 index 000000000..88396f77d --- /dev/null +++ b/coroutines/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/coroutines/src/main/kotlin/com/dbflow5/coroutines/Coroutines.kt b/coroutines/src/main/kotlin/com/dbflow5/coroutines/Coroutines.kt new file mode 100644 index 000000000..feeacdded --- /dev/null +++ b/coroutines/src/main/kotlin/com/dbflow5/coroutines/Coroutines.kt @@ -0,0 +1,130 @@ +package com.dbflow5.coroutines + +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.query.Queriable +import com.dbflow5.structure.delete +import com.dbflow5.structure.insert +import com.dbflow5.structure.load +import com.dbflow5.structure.save +import com.dbflow5.structure.update +import com.dbflow5.transaction.FastStoreModelTransaction +import com.dbflow5.transaction.fastDelete +import com.dbflow5.transaction.fastInsert +import com.dbflow5.transaction.fastSave +import com.dbflow5.transaction.fastUpdate +import kotlinx.coroutines.experimental.CancellableContinuation +import kotlinx.coroutines.experimental.suspendCancellableCoroutine + +/** + * Description: Puts this [Queriable] operation inside a coroutine. Inside the [queriableFunction] + * execute the db operation. + */ +suspend inline fun DBFlowDatabase.awaitTransact( + modelQueriable: Q, + crossinline queriableFunction: Q.() -> R) = suspendCancellableCoroutine { continuation -> + com.dbflow5.coroutines.constructCoroutine(continuation, this) { queriableFunction(modelQueriable) } +} + +inline fun constructCoroutine(continuation: CancellableContinuation, + databaseDefinition: DBFlowDatabase, + crossinline fn: () -> R) { + val transaction = databaseDefinition.beginTransactionAsync { fn() } + .success { _, result -> continuation.resume(result) } + .error { _, throwable -> + if (continuation.isCancelled) return@error + continuation.resumeWithException(throwable) + }.build() + transaction.execute() + + continuation.invokeOnCancellation { + if (continuation.isCancelled) { + transaction.cancel() + } + } +} + + +/** + * Description: Puts a [Model] operation inside a coroutine. Inside the [queriableFunction] + * execute the db operation. + */ +suspend inline fun M.awaitSave(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructCoroutine(continuation, databaseDefinition) { save(databaseDefinition) } +} + +/** + * Description: Puts a [Model] operation inside a coroutine. Inside the [queriableFunction] + * execute the db operation. + */ +suspend inline fun M.awaitInsert(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructCoroutine(continuation, databaseDefinition) { insert(databaseDefinition) } +} + +/** + * Description: Puts a [Model] operation inside a coroutine. Inside the [queriableFunction] + * execute the db operation. + */ +suspend inline fun M.awaitDelete(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructCoroutine(continuation, databaseDefinition) { delete(databaseDefinition) } +} + +/** + * Description: Puts a [Model] operation inside a coroutine. Inside the [queriableFunction] + * execute the db operation. + */ +suspend inline fun M.awaitUpdate(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructCoroutine(continuation, databaseDefinition) { update(databaseDefinition) } +} + +/** + * Description: Puts a [Model] operation inside a coroutine. Inside the [queriableFunction] + * execute the db operation. + */ +suspend inline fun M.awaitLoad(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructCoroutine(continuation, databaseDefinition) { load(databaseDefinition) } +} + +/** + * Description: Puts the [Collection] inside a [FastStoreModelTransaction] coroutine. + */ +suspend inline fun > M.awaitSave(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructFastCoroutine(continuation, databaseDefinition) { fastSave() } +} + +/** + * Description: Puts the [Collection] inside a [FastStoreModelTransaction] coroutine. + */ +suspend inline fun > M.awaitInsert(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructFastCoroutine(continuation, databaseDefinition) { fastInsert() } +} + +/** + * Description: Puts the [Collection] inside a [FastStoreModelTransaction] coroutine. + */ +suspend inline fun > M.awaitUpdate(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructFastCoroutine(continuation, databaseDefinition) { fastUpdate() } +} + +/** + * Description: Puts the [Collection] inside a [FastStoreModelTransaction] coroutine. + */ +suspend inline fun > M.awaitDelete(databaseDefinition: DBFlowDatabase) = suspendCancellableCoroutine { continuation -> + constructFastCoroutine(continuation, databaseDefinition) { fastDelete() } +} + + +inline fun constructFastCoroutine(continuation: CancellableContinuation, + databaseDefinition: DBFlowDatabase, + crossinline fn: () -> FastStoreModelTransaction.Builder) { + val transaction = databaseDefinition.beginTransactionAsync(fn().build()) + .success { _, result -> continuation.resume(result) } + .error { _, throwable -> + if (continuation.isCancelled) return@error + continuation.resumeWithException(throwable) + }.build() + transaction.execute() + + continuation.invokeOnCancellation { + transaction.cancel() + } +} \ No newline at end of file diff --git a/dbflow-core/build.gradle b/dbflow-core/build.gradle deleted file mode 100644 index 8a3deae9b..000000000 --- a/dbflow-core/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -apply plugin: 'java' - -project.ext.artifactId = bt_name - -targetCompatibility = JavaVersion.VERSION_1_7 -sourceCompatibility = JavaVersion.VERSION_1_7 - -apply from: '../java-artifacts.gradle' \ No newline at end of file diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/StringUtils.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/StringUtils.java deleted file mode 100644 index 5079697fb..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/StringUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.raizlabs.android.dbflow; - -/** - * Description: Provides handy method for strings - */ -public class StringUtils { - - /** - * @return true if the string is not null, empty string "", or the length is greater than 0 - */ - public static boolean isNotNullOrEmpty(String inString) { - return inString != null && !inString.equals("") && inString.length() > 0; - } - - /** - * @return true if the string is null, empty string "", or the length is less than equal to 0 - */ - public static boolean isNullOrEmpty(String inString) { - return inString == null || inString.equals("") || inString.length() <= 0; - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Column.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Column.java deleted file mode 100644 index e5b88f9cd..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Column.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Marks a field as corresponding to a column in the DB. - * When adding new columns or changing names, you need to define a new {@link Migration}. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.FIELD) -public @interface Column { - - /** - * @return The name of the column. The default is the field name. - */ - String name() default ""; - - /** - * @return An optional column length - */ - int length() default -1; - - /** - * @return Marks the field as having a specified collation to use in it's creation. - */ - Collate collate() default Collate.NONE; - - /** - * @return Adds a default value for this column when saving. This is a string representation - * of the value. - * Note this will place it in when saving - * to the DB because we cannot know the intention of missing data from a query. - */ - String defaultValue() default ""; - - /** - * @return If private, by default this is get{Name}() for "name". To define a custom one, this method specifies the name - * of the method only, and not any specific params. So for "getAnotherName()" you would use "AnotherName" as the param. - */ - String getterName() default ""; - - /** - * @return If private, by default this is set{Name}() for "name". To define a custom one, this method specifies the name - * of the method only, and not any specific params. So for "setAnotherName(String name)" you would use "AnotherName" as the param. - * The params must align exactly to an expected setter, otherwise a compile error ensues. - */ - String setterName() default ""; - - /** - * @return A custom type converter that's only used for this field. It will be created and used in - * the Adapter associated with this table. - */ - Class typeConverter() default com.raizlabs.android.dbflow.converter.TypeConverter.class; - -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnIgnore.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnIgnore.java deleted file mode 100644 index 100771bdd..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnIgnore.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: An annotation used to ignore a column in the {@link Table#allFields()} instance. - */ -@Retention(RetentionPolicy.CLASS) -@Target(ElementType.FIELD) -public @interface ColumnIgnore { -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnMap.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnMap.java deleted file mode 100644 index 95524fca7..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnMap.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Maps an arbitrary object and its corresponding fields into a set of columns. It is similar - * to {@link ForeignKey} except it's not represented in the DB hierarchy. - */ -@Retention(RetentionPolicy.CLASS) -@Target(ElementType.FIELD) -public @interface ColumnMap { - - /** - * Defines explicit references for a composite {@link ColumnMap} definition. - * - * @return override explicit usage of all fields and provide custom references. - */ - ColumnMapReference[] references() default {}; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnMapReference.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnMapReference.java deleted file mode 100644 index c944d5be4..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ColumnMapReference.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Allows a {@link ColumnMap} to specify a reference override for its fields. Anything not - * defined here will not be used. - */ -@Retention(RetentionPolicy.CLASS) -@Target(ElementType.FIELD) -public @interface ColumnMapReference { - - /** - * @return The local column name that will be referenced in the DB - */ - String columnName(); - - /** - * @return The column name in the referenced table - */ - String columnMapFieldName(); - - /** - * @return Specify the {@link NotNull} annotation here and it will get pasted into the reference definition. - */ - NotNull notNull() default @NotNull(onNullConflict = ConflictAction.NONE); -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Database.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Database.java deleted file mode 100644 index 293edacb0..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Database.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Creates a new database to use in the application. - *

- * If we specify one DB, then all models do not need to specify a DB. As soon as we specify two, then each - * model needs to define what DB it points to. - *

- *

- * Models will specify which DB it belongs to, - * but they currently can only belong to one DB. - *

- */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.SOURCE) -public @interface Database { - - /** - * @return The current version of the DB. Increment it to trigger a DB update. - */ - int version(); - - /** - * @deprecated use DatabaseConfig.databaseName() to change the name. - */ - @Deprecated - String name() default ""; - - /** - * @deprecated use DatabaseConfig.extension() to change the extension. - */ - @Deprecated - String databaseExtension() default ""; - - /** - * @deprecated use DatabaseConfig.inMemoryBuilder() instead. - */ - @Deprecated - boolean inMemory() default false; - - /** - * @return If true, SQLite will throw exceptions when {@link ForeignKey} constraints are not respected. - * Default is false and will not throw exceptions. - */ - boolean foreignKeyConstraintsEnforced() default false; - - /** - * @return Checks for consistency in the DB, if true it will recopy over the prepackage database. - */ - boolean consistencyCheckEnabled() default false; - - /** - * @return Keeps a backup for whenever the database integrity fails a "PRAGMA quick_check(1)" that will - * replace the corrupted DB - */ - boolean backupEnabled() default false; - - /** - * @return Global default insert conflict that can be applied to any table when it leaves - * its {@link ConflictAction} as NONE. - */ - ConflictAction insertConflict() default ConflictAction.NONE; - - /** - * @return Global update conflict that can be applied to any table when it leaves its - * {@link ConflictAction} as NONE - */ - ConflictAction updateConflict() default ConflictAction.NONE; - - /** - * @return Marks all generated classes within this database with this character. For example - * "TestTable" becomes "TestTable$Table" for a "$" separator. - * @deprecated Generated class files will become '_' only in next major release. - */ - @Deprecated - String generatedClassSeparator() default "_"; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKey.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKey.java deleted file mode 100644 index fb82d308c..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKey.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.FIELD) -public @interface ForeignKey { - - /** - * Defines explicit references for a composite {@link ForeignKey} definition. This is no longer required - * as the library will auto-generate references for you based on the other table's primary keys. - * - * @return the set of explicit references if you wish to have different values than default generated. - */ - ForeignKeyReference[] references() default {}; - - /** - * @return Default false. When this column is a {@link ForeignKey} and table object, - * returning true will save the model before adding the fields to save as a foreign key. - * If false, we expect the field to not change and must save the model manually outside - * of the ModelAdapter before saving the child class. - */ - boolean saveForeignKeyModel() default false; - - /** - * @return Default false. When this column is a {@link ForeignKey} and table object, - * returning true will delte the model before deleting its enclosing child class. - * If false, we expect the field to not change and must delete the model manually outside - * of the ModelAdapter before saving the child class. - */ - boolean deleteForeignKeyModel() default false; - - /** - * @return Replaces legacy ForeignKeyContainer, this method instructs the code generator to only - * populate the model with the {@link ForeignKeyReference} defined in this field. This skips - * the Select retrieval convenience. - */ - boolean stubbedRelationship() default false; - - /** - * @return If true, during a transaction, FK constraints are not violated immediately until the resulting transaction commits. - * This is useful for out of order foreign key operations. - * @see Deferred Foreign Key Constraints - */ - boolean deferred() default false; - - /** - * @return an optional table class that this reference points to. It's only used if the field - * is NOT a Model class. - */ - Class tableClass() default Object.class; - - /** - * Defines {@link ForeignKeyAction} action to be performed - * on delete of referenced record. Defaults to {@link ForeignKeyAction#NO_ACTION}. Used only when - * columnType is {@link ForeignKey}. - * - * @return {@link ForeignKeyAction} - */ - ForeignKeyAction onDelete() default ForeignKeyAction.NO_ACTION; - - /** - * Defines {@link ForeignKeyAction} action to be performed - * on update of referenced record. Defaults to {@link ForeignKeyAction#NO_ACTION}. Used only when - * columnType is {@link ForeignKey}. - * - * @return {@link ForeignKeyAction} - */ - ForeignKeyAction onUpdate() default ForeignKeyAction.NO_ACTION; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKeyReference.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKeyReference.java deleted file mode 100644 index 7cb343b33..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ForeignKeyReference.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Description: Used inside of {@link ForeignKey#references()}, describes the - * local column name, type, and referencing table column name. - *

- * Note: the type of the local column must match the - * column type of the referenced column. By using a field as a Model object, - * you will need to ensure the same types are used. - */ -@Retention(RetentionPolicy.SOURCE) -public @interface ForeignKeyReference { - - /** - * @return The local column name that will be referenced in the DB - */ - String columnName(); - - /** - * @return The column name in the referenced table - */ - String foreignKeyColumnName(); - - /** - * @return Specify the {@link NotNull} annotation here and it will get pasted into the reference definition. - */ - NotNull notNull() default @NotNull(onNullConflict = ConflictAction.NONE); -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Index.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Index.java deleted file mode 100644 index 7939773e1..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Index.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Creates an index for a specified {@link Column}. A single column can belong to multiple - * indexes within the same table if you wish. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.FIELD) -public @interface Index { - - /** - * @return The set of index groups that this index belongs to. - */ - int[] indexGroups() default {}; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/IndexGroup.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/IndexGroup.java deleted file mode 100644 index b4eb07548..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/IndexGroup.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: - */ -@Target(ElementType.ANNOTATION_TYPE) -@Retention(RetentionPolicy.SOURCE) -public @interface IndexGroup { - - int GENERIC = -1; - - /** - * @return The number that each contained {@link Index} points to, so they can be combined into a single index. - * If {@link #GENERIC}, this will assume a generic index that covers the whole table. - */ - int number() default GENERIC; - - /** - * @return The name of this index. It must be unique from other {@link IndexGroup}. - */ - String name(); - - /** - * @return If true, this will disallow duplicate values to be inserted into the table. - */ - boolean unique() default false; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/InheritedColumn.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/InheritedColumn.java deleted file mode 100644 index 158ec83ec..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/InheritedColumn.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Allows {@link Table} to inherit fields from other objects to make it part of the DB table. - */ -@Target(ElementType.ANNOTATION_TYPE) -@Retention(RetentionPolicy.SOURCE) -public @interface InheritedColumn { - - /** - * @return The column annotation as if it was part of the class - */ - Column column(); - - /** - * @return The field name that an inherited column uses. It must match exactly case-by-case to the field you're referencing. - * If the field is private, the {@link Column} allows you to define getter and setters for it. - */ - String fieldName(); - - /** - * @return If specified other than {@link ConflictAction#NONE}, then we assume {@link NotNull}. - */ - ConflictAction nonNullConflict() default ConflictAction.NONE; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/InheritedPrimaryKey.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/InheritedPrimaryKey.java deleted file mode 100644 index 954e992e1..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/InheritedPrimaryKey.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Allows you to specify a non-Column to be inherited and used as a {@link PrimaryKey} - */ -@Target(ElementType.ANNOTATION_TYPE) -@Retention(RetentionPolicy.SOURCE) -public @interface InheritedPrimaryKey { - - /** - * @return The primary key annotation as if it was part of the class - */ - PrimaryKey primaryKey(); - - /** - * @return The column annotation as if it was part of the class - */ - Column column(); - - /** - * @return The field name that an inherited column uses. It must match exactly case-by-case to the field you're referencing. - * If the field is private, the {@link PrimaryKey} allows you to define getter and setters for it. - */ - String fieldName(); -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ManyToMany.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ManyToMany.java deleted file mode 100644 index 731c7ca2a..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ManyToMany.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Builds a many-to-many relationship with another {@link Table}. Only one table needs to specify - * the annotation and its assumed that they use primary keys only. The generated - * class will contain an auto-incrementing primary key by default. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface ManyToMany { - - /** - * @return The other table class by which this will get merged. - */ - Class referencedTable(); - - /** - * @return A name that we use as the column name for the referenced table in the - * generated ManyToMany table class. - */ - String referencedTableColumnName() default ""; - - /** - * @return A name that we use as the column name for this specific table's name. - */ - String thisTableColumnName() default ""; - - /** - * @return By default, we generate an auto-incrementing {@link Long} {@link PrimaryKey}. - * If false, all {@link PrimaryKey} of the corresponding tables will be placed as {@link ForeignKey} and {@link PrimaryKey} - * of the generated table instead of using an autoincrementing Long {@link PrimaryKey}. - */ - boolean generateAutoIncrement() default true; - - /** - * @return by default, we append {selfTable}{generatedClassSeparator}{referencedTable} or "User_Follower", - * for example. If you want different name, change this. - */ - String generatedTableClassName() default ""; - - /** - * @return by default the Models referenced here are not saved prior to saving this - * object for obvious efficiency reasons. - * @see ForeignKey#saveForeignKeyModel() - */ - boolean saveForeignKeyModels() default false; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Migration.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Migration.java deleted file mode 100644 index 56af5bfad..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Migration.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Marks a Migration class to be included in DB construction. The class using this annotation - * must implement the Migration interface. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface Migration { - - /** - * @return The version the migration will trigger at. - */ - int version(); - - /** - * @return Specify the database class that this migration belongs to. - */ - Class database(); - - /** - * @return If number greater than -1, the migrations from the same {@link #version()} get ordered from - * highest to lowest. if they are the same priority, there is no telling which one is executed first. The - * annotation processor will process in order it finds the classes. - */ - int priority() default -1; - -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelCacheField.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelCacheField.java deleted file mode 100644 index 9bd97db3f..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelCacheField.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: marks a single field as a ModelCache creator that is used in the corresponding ModelAdapter. - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.SOURCE) -public @interface ModelCacheField { -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelView.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelView.java deleted file mode 100644 index 67e43f24b..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelView.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import com.raizlabs.android.dbflow.sql.Query; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Author: andrewgrosner - * Description: Marks a class as being an SQL VIEW definition. It must extend BaseModelView and have - * a single public, static, final field that is annotated with {@link ModelViewQuery} and be a {@link Query}. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface ModelView { - - /** - * @return The name of this view. Default is the class name. - */ - String name() default ""; - - /** - * @return The class of the database this corresponds to. - */ - Class database(); - - - /** - * @return When true, all public, package-private , non-static, and non-final fields of the reference class are considered as {@link com.raizlabs.android.dbflow.annotation.Column} . - * The only required annotated field becomes The {@link PrimaryKey} - * or {@link PrimaryKey#autoincrement()}. - */ - boolean allFields() default false; - - /** - * @return The higher the number, the order by which the creation of this class gets called. - * Useful for creating ones that depend on another {@link ModelView}. - */ - int priority() default 0; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelViewQuery.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelViewQuery.java deleted file mode 100644 index 351d4d7ec..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/ModelViewQuery.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import com.raizlabs.android.dbflow.sql.Query; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Represents a field that is a {@link Query}. This is only meant to be used as a query - * reference in {@link ModelView}. This is so the annotation processor knows how to access the query of - * the view. - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.SOURCE) -public @interface ModelViewQuery { -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/MultiCacheField.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/MultiCacheField.java deleted file mode 100644 index 31006644d..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/MultiCacheField.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Marks a field as the IMultiKeyCacheModel that we use to convert multiple fields into - * a single key for caching. - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.SOURCE) -public @interface MultiCacheField { -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/MultipleManyToMany.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/MultipleManyToMany.java deleted file mode 100644 index d57b2acee..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/MultipleManyToMany.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Provides ability to add multiple {@link ManyToMany} annotations at once. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface MultipleManyToMany { - - ManyToMany[] value(); -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/NotNull.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/NotNull.java deleted file mode 100644 index 982af7fb6..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/NotNull.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Specifies that a {@link Column} is not null. - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.SOURCE) -public @interface NotNull { - - /** - * Defines how to handle conflicts for not null column - * - * @return a {@link com.raizlabs.android.dbflow.annotation.ConflictAction} enum - */ - ConflictAction onNullConflict() default ConflictAction.FAIL; - -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/OneToMany.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/OneToMany.java deleted file mode 100644 index 175a8cadf..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/OneToMany.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.List; - -/** - * Description: Describes a 1-many relationship. It applies to some method that returns a {@link List} of Model objects. - * This annotation can handle loading, deleting, and saving when the current data changes. By default it will call the - * associated method when the containing class operates. - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.SOURCE) -public @interface OneToMany { - - /** - * The method to apply the OneToMany to. - */ - enum Method { - - /** - * Load this relationship when the parent model loads from the database. This is called before the OnLoadFromCursor - * method, but after other columns load. - */ - LOAD, - - /** - * Inserts code to delete the results returned from the List relationship when the parent model is deleted. - */ - DELETE, - - /** - * Inserts code to save the list of models when the parent model is saved. - */ - SAVE, - - /** - * Shorthand to support all options. - */ - ALL - } - - /** - * @return The methods you wish to call it from. By default it's loaded out of the DB. - */ - Method[] methods() default Method.LOAD; - - /** - * @return The name of the list variable to use. If is left blank, we will remove the "get" and then decapitalize the remaining name. - */ - String variableName() default ""; - - /** - * @return If true, the underlying variable that we use is private, requiring us to provide - * a setter for it. - * @deprecated has no effect on the visibility of the call since we now autodetect visibility. - */ - @Deprecated - boolean isVariablePrivate() default false; - - /** - * @return If true, the code generated for this relationship done as efficiently as possible. - * It will not work on nested relationships, caching, and other code that requires overriding of BaseModel or Model operations. - */ - boolean efficientMethods() default true; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/PrimaryKey.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/PrimaryKey.java deleted file mode 100644 index 2d2521412..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/PrimaryKey.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.FIELD) -public @interface PrimaryKey { - - /** - * Specifies if the column is autoincrementing or not - */ - boolean autoincrement() default false; - - /** - * Specifies the column to be treated as a ROWID but is not an {@link #autoincrement()}. This - * overrides {@link #autoincrement()} and is mutually exclusive. - */ - boolean rowID() default false; - - /** - * @return When true, we simple do {columnName} > 0 when checking for it's existence if {@link #autoincrement()} - * is true. If not, we do a full database SELECT exists. - */ - boolean quickCheckAutoIncrement() default false; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/QueryModel.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/QueryModel.java deleted file mode 100644 index d7295582e..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/QueryModel.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Marks a Model class as NOT a {@link Table}, but generates code for retrieving data from a - * generic query - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface QueryModel { - - /** - * @return Specify the class of the database to use. - */ - Class database(); - - /** - * @return If true, all accessible, non-static, and non-final fields are treated as valid fields. - * @see Table#allFields() - */ - boolean allFields() default false; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Table.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Table.java deleted file mode 100644 index 106e37aed..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Table.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Author: andrewgrosner - * Description: Marks a class as being a table for only ONE DB. It must implement the Model interface and all fields MUST be package private. - * This will generate a $Table and $Adapter class. The $Table class generates static final column name variables to reference in queries. - * The $Adapter class defines how to retrieve and store this object as well as other methods for acting on model objects in the database. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface Table { - - int DEFAULT_CACHE_SIZE = 25; - - /** - * @return Specifies a different name for the table than the name of the Model class. - */ - String name() default ""; - - /** - * @return Specify the database class that this table belongs to. It must have the {@link Database} annotation. - */ - Class database(); - - /** - * @return Specify the general conflict algorithm used by this table when updating records. - */ - ConflictAction updateConflict() default ConflictAction.NONE; - - /** - * @return Specify the general insert conflict algorithm used by this table. - */ - ConflictAction insertConflict() default ConflictAction.NONE; - - /** - * @return An optional {@link ConflictAction} that we append to creation for conflict handling in PK. - */ - ConflictAction primaryKeyConflict() default ConflictAction.NONE; - - /** - * @return When true, all public, package-private , non-static, and non-final fields of the reference class are considered as {@link com.raizlabs.android.dbflow.annotation.Column} . - * The only required annotated field becomes The {@link PrimaryKey} - * or {@link PrimaryKey#autoincrement()}. - */ - boolean allFields() default false; - - /** - * @return If true, all private boolean fields will use "is" instead of "get" for its getter and - * "set" without the "is" if it starts with "is" - */ - boolean useBooleanGetterSetters() default true; - - /** - * @return If true, caching mechanism is enabled. This works for single primary key tables. For - * multi-primary key tables, IMultiKeyCacheModel interface is required to specify the caching key. - */ - boolean cachingEnabled() default false; - - /** - * @return If true, we throw away checks for column indexing and simply assume that the cursor returns - * all our columns in order. This may provide a slight performance boost. - */ - boolean orderedCursorLookUp() default false; - - /** - * @return When true, we reassign the corresponding Model's fields to default values when loading - * from cursor. If false, we assign values only if present in Cursor. - */ - boolean assignDefaultValuesFromCursor() default true; - - /** - * @return When false, this table gets generated and associated with database, however it will not immediately - * get created upon startup. This is useful for keeping around legacy tables for migrations. - */ - boolean createWithDatabase() default true; - - /** - * @return The cache size for this Table. - */ - int cacheSize() default 25; - - /** - * @return Declares a set of UNIQUE columns with the corresponding {@link ConflictAction}. A {@link Column} - * will point to this group using {@link Unique#uniqueGroups()} - */ - UniqueGroup[] uniqueColumnGroups() default {}; - - /** - * @return The set of INDEX clauses that specific columns can define to belong to, using the {@link Index} annotation. - * The generated Index properties belong to the corresponding property class to this table. - */ - IndexGroup[] indexGroups() default {}; - - /** - * @return A set of inherited accessible fields not necessarily defined as columns in the super class of this table. - * Each must be accessible via: public, package private, or protected or getter/setters. - */ - InheritedColumn[] inheritedColumns() default {}; - - /** - * @return A set of inherited accessible fields not necessarily defined as columns in the super class of this table. - * Each must be accessible via: public, package private, or protected or getter/setters. - */ - InheritedPrimaryKey[] inheritedPrimaryKeys() default {}; - -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/TypeConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/TypeConverter.java deleted file mode 100644 index a99bfaa57..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/TypeConverter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Author: andrewgrosner - * Description: Marks a class as being a TypeConverter. A type converter will turn a non-model, non-SQLiteTyped class into - * a valid database type. - */ -@Retention(RetentionPolicy.CLASS) -@Target(ElementType.TYPE) -public @interface TypeConverter { - - /** - * @return Specify a set of subclasses by which the {@link TypeConverter} registers for. For - * each one, this will create a new instance of the converter. - */ - Class[] allowedSubtypes() default {}; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Unique.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Unique.java deleted file mode 100644 index 35e762e79..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/Unique.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Marks the field as unique, meaning its value cannot be repeated. It is, however, - * NOT a primary key. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.FIELD) -public @interface Unique { - - /** - * @return if field is unique. If false, we expect {@link #uniqueGroups()} to be specified.` - */ - boolean unique() default true; - - /** - * @return Marks a unique field as part of a unique group. For every unique number entered, - * it will generate a UNIQUE() column statement. - */ - int[] uniqueGroups() default {}; - - /** - * Defines how to handle conflicts for a unique column - * - * @return a {@link com.raizlabs.android.dbflow.annotation.ConflictAction} enum - */ - ConflictAction onUniqueConflict() default ConflictAction.FAIL; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/UniqueGroup.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/UniqueGroup.java deleted file mode 100644 index b307ed387..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/UniqueGroup.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.raizlabs.android.dbflow.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: - */ -@Target(ElementType.ANNOTATION_TYPE) -@Retention(RetentionPolicy.SOURCE) -public @interface UniqueGroup { - - /** - * @return The number that columns point to to use this group - */ - int groupNumber(); - - /** - * @return The conflict action that this group takes. - */ - ConflictAction uniqueConflict() default ConflictAction.FAIL; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/ContentProvider.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/ContentProvider.java deleted file mode 100644 index e3a8548dd..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/ContentProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.raizlabs.android.dbflow.annotation.provider; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Defines a Content Provider that gets generated. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.SOURCE) -public @interface ContentProvider { - - /** - * @return The authority URI for this provider. - */ - String authority(); - - /** - * @return The class of the database this belongs to - */ - Class database(); - - /** - * @return The base content uri String to use for all paths - */ - String baseContentUri() default ""; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/ContentUri.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/ContentUri.java deleted file mode 100644 index 1b12491e2..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/ContentUri.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.raizlabs.android.dbflow.annotation.provider; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Defines the URI for a content provider. - */ -@Target({ElementType.FIELD, ElementType.METHOD}) -@Retention(RetentionPolicy.SOURCE) -public @interface ContentUri { - - /** - * Provides some handy constants for defining a {@link #type()} - */ - class ContentType { - - public static final String VND_MULTIPLE = "vnd.android.cursor.dir/"; - - public static final String VND_SINGLE = "vnd.android.cursor.item/"; - } - - /** - * Defines the path segment that we use when we specify "#" in the path. - */ - @interface PathSegment { - - /** - * @return The number segment this corresponds to. - */ - int segment(); - - /** - * @return The column name that this segment will use. - */ - String column(); - } - - /** - * @return the path of this ContentUri. ex: notes/#, notes/1, etc. Must be unique within a {@link TableEndpoint} - */ - String path(); - - /** - * @return The type of content that this uri is associated with. Ex: {@link ContentType#VND_SINGLE} - */ - String type(); - - /** - * @return If the path defines "#", then we use these numbers to find them in the same order as - * where column. - */ - PathSegment[] segments() default {}; - - /** - * @return false if you wish to not allow queries from the specified URI. - */ - boolean queryEnabled() default true; - - /** - * @return false if you wish to prevent inserts. - */ - boolean insertEnabled() default true; - - /** - * @return false if you wish to prevent deletion. - */ - boolean deleteEnabled() default true; - - /** - * @return false if you wish to prevent updates. - */ - boolean updateEnabled() default true; - -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/Notify.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/Notify.java deleted file mode 100644 index e678d8dae..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/Notify.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.raizlabs.android.dbflow.annotation.provider; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Annotates a method part of {@link com.raizlabs.android.dbflow.annotation.provider.TableEndpoint} - * that gets called back when changed. The method must return a Uri or an array of Uri[] to notify changed on - * the content provider. - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.METHOD) -public @interface Notify { - - enum Method { - INSERT, - UPDATE, - DELETE - } - - /** - * @return The {@link com.raizlabs.android.dbflow.annotation.provider.Notify.Method} notify - */ - Method method(); - - /** - * @return Registers itself for the following paths. If a specific path is called for the specified - * method, the method this annotation corresponds to will be called. - */ - String[] paths() default {}; -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/TableEndpoint.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/TableEndpoint.java deleted file mode 100644 index 7251708f0..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/annotation/provider/TableEndpoint.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.raizlabs.android.dbflow.annotation.provider; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Description: Defines an endpoint that gets placed inside of a {@link com.raizlabs.android.dbflow.annotation.provider.ContentProvider} - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.SOURCE) -public @interface TableEndpoint { - - /** - * @return The name of the table this endpoint corresponds to. - */ - String name(); - - /** - * @return When placed in a top-level class, this is required to connect it to a provider. - */ - Class contentProvider(); -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BigDecimalConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BigDecimalConverter.java deleted file mode 100644 index f06249e92..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BigDecimalConverter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -import java.math.BigDecimal; - -/** - * Description: Defines how we store and retrieve a {@link java.math.BigDecimal} - */ -public class BigDecimalConverter extends TypeConverter { - @Override - public String getDBValue(BigDecimal model) { - return model == null ? null : model.toString(); - } - - @Override - public BigDecimal getModelValue(String data) { - return data == null ? null : new BigDecimal(data); - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BigIntegerConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BigIntegerConverter.java deleted file mode 100644 index 85826246b..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BigIntegerConverter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -import java.math.BigInteger; - -/** - * Description: Defines how we store and retrieve a {@link java.math.BigInteger} - */ -public class BigIntegerConverter extends TypeConverter { - @Override - public String getDBValue(BigInteger model) { - return model == null ? null : model.toString(); - } - - @Override - public BigInteger getModelValue(String data) { - return data == null ? null : new BigInteger(data); - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BooleanConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BooleanConverter.java deleted file mode 100644 index d44a0c34e..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/BooleanConverter.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -/** - * Description: Converts a boolean object into an Integer for database storage. - */ -public class BooleanConverter extends TypeConverter { - @Override - public Integer getDBValue(Boolean model) { - return model == null ? null : model ? 1 : 0; - } - - @Override - public Boolean getModelValue(Integer data) { - return data == null ? null : data == 1; - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CalendarConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CalendarConverter.java deleted file mode 100644 index 2832262c2..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CalendarConverter.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -import java.util.Calendar; -import java.util.GregorianCalendar; - -/** - * Author: andrewgrosner - * Description: Defines how we store and retrieve a {@link java.util.Calendar} - */ -@com.raizlabs.android.dbflow.annotation.TypeConverter(allowedSubtypes = {GregorianCalendar.class}) -public class CalendarConverter extends TypeConverter { - - @Override - public Long getDBValue(Calendar model) { - return model == null ? null : model.getTimeInMillis(); - } - - @Override - public Calendar getModelValue(Long data) { - if (data != null) { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(data); - return calendar; - } else { - return null; - } - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CharConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CharConverter.java deleted file mode 100644 index f7bf81692..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/CharConverter.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -/** - * Description: Converts a {@link Character} into a {@link String} for database storage. - */ -public class CharConverter extends TypeConverter { - - @Override - public String getDBValue(Character model) { - return model != null ? new String(new char[]{model}) : null; - } - - @Override - public Character getModelValue(String data) { - return data != null ? data.charAt(0) : null; - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/DateConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/DateConverter.java deleted file mode 100644 index 43475b1e0..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/DateConverter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -import java.util.Date; - -/** - * Author: andrewgrosner - * Description: Defines how we store and retrieve a {@link java.util.Date} - */ -public class DateConverter extends TypeConverter { - - @Override - public Long getDBValue(Date model) { - return model == null ? null : model.getTime(); - } - - @Override - public Date getModelValue(Long data) { - return data == null ? null : new Date(data); - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/SqlDateConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/SqlDateConverter.java deleted file mode 100644 index b1fa0a7b0..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/SqlDateConverter.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -import java.sql.Date; -import java.sql.Time; -import java.sql.Timestamp; - -/** - * Author: andrewgrosner - * Description: Defines how we store and retrieve a {@link java.sql.Date} - */ -@com.raizlabs.android.dbflow.annotation.TypeConverter(allowedSubtypes = {Time.class, Timestamp.class}) -public class SqlDateConverter extends TypeConverter { - - @Override - public Long getDBValue(Date model) { - return model == null ? null : model.getTime(); - } - - @Override - public Date getModelValue(Long data) { - return data == null ? null : new Date(data); - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/TypeConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/TypeConverter.java deleted file mode 100644 index f755ae0b1..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/TypeConverter.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -/** - * Author: andrewgrosner - * Description: This class is responsible for converting the stored database value into the field value in - * a Model. - */ -@com.raizlabs.android.dbflow.annotation.TypeConverter -public abstract class TypeConverter { - - /** - * Converts the ModelClass into a DataClass - * - * @param model this will be called upon syncing - * @return The DataClass value that converts into a SQLite type - */ - public abstract DataClass getDBValue(ModelClass model); - - /** - * Converts a DataClass from the DB into a ModelClass - * - * @param data This will be called when the model is loaded from the DB - * @return The ModelClass value that gets set in a Model that holds the data class. - */ - public abstract ModelClass getModelValue(DataClass data); -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/UUIDConverter.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/UUIDConverter.java deleted file mode 100644 index dc26fbe3d..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/converter/UUIDConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.raizlabs.android.dbflow.converter; - -import java.util.UUID; - -/** - * Description: Responsible for converting a {@link UUID} to a {@link String}. - * - * @author Andrew Grosner (fuzz) - */ -public class UUIDConverter extends TypeConverter { - - @Override - public String getDBValue(UUID model) { - return model != null ? model.toString() : null; - } - - @Override - public UUID getModelValue(String data) { - if (data == null) { - return null; - } - return UUID.fromString(data); - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/data/Blob.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/data/Blob.java deleted file mode 100644 index b879b7ef1..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/data/Blob.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.raizlabs.android.dbflow.data; - -/** - * Description: Provides a way to support blob format data. - */ -public class Blob { - - private byte[] blob; - - public Blob() { - } - - public Blob(byte[] blob) { - this.blob = blob; - } - - /** - * Sets the underlying blob data. - * - * @param blob The set of bytes to use. - */ - public void setBlob(byte[] blob) { - this.blob = blob; - } - - /** - * @return The blob data. - */ - public byte[] getBlob() { - return blob; - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java deleted file mode 100644 index ec85bbed2..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/QueryBuilder.java +++ /dev/null @@ -1,328 +0,0 @@ -package com.raizlabs.android.dbflow.sql; - -import java.util.List; -import java.util.regex.Pattern; - -/** - * Description: This is used as a wrapper around {@link StringBuilder} in order to provide more - * database focused methods and to assist in generating queries to the DB using our SQL wrappers. - */ -public class QueryBuilder implements Query { - - private static final char QUOTE = '`'; - - private static final Pattern QUOTE_PATTERN = Pattern.compile(QUOTE + ".*" + QUOTE); - - /** - * This query is backed by a {@link StringBuilder} - */ - protected StringBuilder query = new StringBuilder(); - - /** - * Constructs this item with an empty {@link StringBuilder} - */ - public QueryBuilder() { - super(); - } - - /** - * Constructs this class with the specified String - * - * @param object The string to append - */ - public QueryBuilder(Object object) { - append(object); - } - - /** - * Appends a space to this query - * - * @return This instance - */ - public QueryClass appendSpace() { - return append(" "); - } - - /** - * Appends the string with spaces on the front and end of the string - * - * @param object The object to append - * @return This instance - */ - @SuppressWarnings("unchecked") - public QueryClass appendSpaceSeparated(Object object) { - return (QueryClass) appendSpace().append(object).appendSpace(); - } - - /** - * Appends the string as (string) - * - * @param string The string to append - * @return This instance - */ - @SuppressWarnings("unchecked") - public QueryClass appendParenthesisEnclosed(Object string) { - return (QueryClass) append("(").append(string).append(")"); - } - - /** - * Appends an object to this query - * - * @param object The object to append - * @return This instance - */ - public QueryClass append(Object object) { - query.append(object); - return castThis(); - } - - /** - * Appends the object only if its not null - * - * @param object If not null, its string representation. - * @return This instance - */ - public QueryClass appendOptional(Object object) { - if (object != null) { - append(object); - } - return castThis(); - } - - /** - * Casts the current object to the {@link QueryClass} - * - * @return This casted instance - */ - @SuppressWarnings("unchecked") - protected QueryClass castThis() { - return (QueryClass) this; - } - - /** - * Appends an {@link SQLiteType} to this query based on the class - * passed in. - * - * @param type The Class to look up from {@link SQLiteType} - * @return This instance - */ - public QueryClass appendType(String type) { - return appendSQLiteType(SQLiteType.get(type)); - } - - /** - * Appends the {@link SQLiteType} to this query - * - * @param sqLiteType The {@link SQLiteType} to append - * @return This instance - */ - public QueryClass appendSQLiteType(SQLiteType sqLiteType) { - return append(sqLiteType.name()); - } - - /** - * Appends an array of these objects by joining them with a comma with - * {@link #join(CharSequence, Object[])} - * - * @param objects The array of objects to pass in - * @return This instance - */ - public QueryClass appendArray(Object... objects) { - return append(join(", ", objects)); - } - - /** - * Appends a list of objects by joining them with a comma with - * {@link #join(CharSequence, Iterable)} - * - * @param objects The list of objects to pass in - * @return This instance - */ - public QueryClass appendList(List objects) { - return append(join(", ", objects)); - } - - /** - * Appends a value only if it's not empty or null - * - * @param name The name of the qualifier - * @param value The value to append after the name - * @return This instance - */ - public QueryClass appendQualifier(String name, String value) { - if (value != null && value.length() > 0) { - if (name != null) { - append(name); - } - appendSpaceSeparated(value); - } - return castThis(); - } - - /** - * Only appends the text if it is not null or empty - * - * @param string The string to append - * @return This instance - */ - public QueryClass appendNotEmpty(String string) { - if (string != null && !string.isEmpty()) { - append(string); - } - return castThis(); - } - - /** - * Appends a quoted string. If the string is the all symbol '*', we do not quote it. - * - * @param string The string to append - * @return This instance - */ - @SuppressWarnings("unchecked") - public QueryClass appendQuoted(String string) { - if (string.equals("*")) - return append(string); - - append(quote(string)); - return castThis(); - } - - /** - * Appends a quoted string if needed. If the string is the all symbol '*', we do not quote it. - * - * @param string The string to append - * @return This instance - */ - @SuppressWarnings("unchecked") - public QueryClass appendQuotedIfNeeded(String string) { - if (string.equals("*")) - return append(string); - - append(quoteIfNeeded(string)); - return castThis(); - } - - /** - * Appends a list of objects by quoting and joining them with a comma with - * {@link #join(CharSequence, Iterable)} - * - * @param objects The list of objects to pass in - * @return This instance - */ - @SuppressWarnings("unchecked") - public QueryClass appendQuotedList(List objects) { - return appendQuoted(join("`, `", objects)); - } - - /** - * Appends an array of these objects by quoting and joining them with a comma with - * {@link #join(CharSequence, Object[])} - * - * @param objects The array of objects to pass in - * @return This instance - */ - public QueryClass appendQuotedArray(Object... objects) { - return appendQuoted(join("`, `", objects)); - } - - @Override - public String toString() { - return getQuery(); - } - - @Override - public String getQuery() { - return query.toString(); - } - - /** - * @param columnName The column name to use. - * @return A name in quotes. E.G. index => `index` so we can use keywords as column names without fear - * of clashing. - */ - public static String quote(String columnName) { - return QUOTE + columnName.replace(".", "`.`") + QUOTE; - } - - /** - * Quotes the identifier if its not already quoted. - * - * @param name The name of the column or table. - * @return Quoted only once. - */ - public static String quoteIfNeeded(String name) { - if (name != null && !isQuoted(name)) { - return quote(name); - } else { - return name; - } - } - - /** - * Helper method to check if name is quoted. - * - * @param name The name of a column or table. - * @return true if the name is quoted. We may not want to quote something if its already so. - */ - public static boolean isQuoted(String name) { - return (QUOTE_PATTERN.matcher(name).find()); - } - - /** - * Strips quotes out of a identifier if need to do so. - * - * @param name The name ot strip the quotes from. - * @return A non-quoted name. - */ - public static String stripQuotes(String name) { - String ret = name; - if (ret != null && isQuoted(ret)) { - ret = ret.replace("`", ""); - } - return ret; - } - - /** - * Returns a string containing the tokens joined by delimiters. - * - * @param delimiter The text to join the text with. - * @param tokens an array objects to be joined. Strings will be formed from - * the objects by calling object.toString(). - * @return A joined string - */ - public static String join(CharSequence delimiter, Object[] tokens) { - StringBuilder sb = new StringBuilder(); - boolean firstTime = true; - for (Object token : tokens) { - if (firstTime) { - firstTime = false; - } else { - sb.append(delimiter); - } - sb.append(token); - } - return sb.toString(); - } - - /** - * Returns a string containing the tokens joined by delimiters. - * - * @param delimiter The text to join the text with. - * @param tokens an array objects to be joined. Strings will be formed from - * the objects by calling object.toString(). - * @return A joined string - */ - public static String join(CharSequence delimiter, Iterable tokens) { - StringBuilder sb = new StringBuilder(); - boolean firstTime = true; - for (Object token : tokens) { - if (firstTime) { - firstTime = false; - } else { - sb.append(delimiter); - } - sb.append(token); - } - return sb.toString(); - } -} diff --git a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/SQLiteType.java b/dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/SQLiteType.java deleted file mode 100644 index be56fa2af..000000000 --- a/dbflow-core/src/main/java/com/raizlabs/android/dbflow/sql/SQLiteType.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.raizlabs.android.dbflow.sql; - -import com.raizlabs.android.dbflow.data.Blob; - -import java.util.HashMap; -import java.util.Map; - -/** - * Description: Represents a type that SQLite understands. - */ -public enum SQLiteType { - - /** - * Represents an integer number in the DB. - */ - INTEGER, - - /** - * Represents a floating-point, precise number. - */ - REAL, - - /** - * Represents text. - */ - TEXT, - - /** - * A column defined by {@link byte[]} data. Usually reserved for images or larger pieces of content. - */ - BLOB; - - private static final Map sTypeMap = new HashMap() { - { - put(byte.class.getName(), SQLiteType.INTEGER); - put(short.class.getName(), SQLiteType.INTEGER); - put(int.class.getName(), SQLiteType.INTEGER); - put(long.class.getName(), SQLiteType.INTEGER); - put(float.class.getName(), SQLiteType.REAL); - put(double.class.getName(), SQLiteType.REAL); - put(boolean.class.getName(), SQLiteType.INTEGER); - put(char.class.getName(), SQLiteType.TEXT); - put(byte[].class.getName(), SQLiteType.BLOB); - put(Byte.class.getName(), SQLiteType.INTEGER); - put(Short.class.getName(), SQLiteType.INTEGER); - put(Integer.class.getName(), SQLiteType.INTEGER); - put(Long.class.getName(), SQLiteType.INTEGER); - put(Float.class.getName(), SQLiteType.REAL); - put(Double.class.getName(), SQLiteType.REAL); - put(Boolean.class.getName(), SQLiteType.INTEGER); - put(Character.class.getName(), SQLiteType.TEXT); - put(CharSequence.class.getName(), SQLiteType.TEXT); - put(String.class.getName(), SQLiteType.TEXT); - put(Byte[].class.getName(), SQLiteType.BLOB); - put(Blob.class.getName(), SQLiteType.BLOB); - } - }; - - /** - * Returns the {@link SQLiteType} for this class - * - * @param className The fully qualified class name - * @return The type from the class name - */ - public static SQLiteType get(String className) { - return sTypeMap.get(className); - } - - public static boolean containsClass(String className) { - return sTypeMap.containsKey(className); - } -} diff --git a/dbflow-kotlinextensions/build.gradle b/dbflow-kotlinextensions/build.gradle deleted file mode 100644 index 4de4f19a1..000000000 --- a/dbflow-kotlinextensions/build.gradle +++ /dev/null @@ -1,36 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -project.ext.artifactId = bt_name - - -android { - compileSdkVersion Integer.valueOf(dbflow_target_sdk) - buildToolsVersion dbflow_build_tools_version - - defaultConfig { - minSdkVersion dbflow_min_sdk - targetSdkVersion Integer.valueOf(dbflow_target_sdk) - } - buildTypes { - debug { - minifyEnabled false - } - release { - minifyEnabled false - } - } - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } -} - -dependencies { - api "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - api project("${dbflow_project_prefix}dbflow-core") - api project("${dbflow_project_prefix}dbflow") -} - -apply from: '../kotlin-artifacts.gradle' - - diff --git a/dbflow-kotlinextensions/gradle.properties b/dbflow-kotlinextensions/gradle.properties deleted file mode 100644 index bca645ff5..000000000 --- a/dbflow-kotlinextensions/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -bt_name=dbflow-kotlinextensions -bt_packaging=aar -bt_artifact_id=dbflow-kotlinextensions \ No newline at end of file diff --git a/dbflow-kotlinextensions/proguard-rules.pro b/dbflow-kotlinextensions/proguard-rules.pro deleted file mode 100644 index d31a3fb13..000000000 --- a/dbflow-kotlinextensions/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /Users/andrewgrosner/Documents/sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/dbflow-kotlinextensions/src/main/AndroidManifest.xml b/dbflow-kotlinextensions/src/main/AndroidManifest.xml deleted file mode 100644 index e1050c1a7..000000000 --- a/dbflow-kotlinextensions/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/DatabaseExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/DatabaseExtensions.kt deleted file mode 100644 index 5c37b9ed4..000000000 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/DatabaseExtensions.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.raizlabs.android.dbflow.kotlinextensions - -import android.content.ContentValues -import com.raizlabs.android.dbflow.config.DatabaseDefinition -import com.raizlabs.android.dbflow.config.FlowManager.* -import com.raizlabs.android.dbflow.structure.Model -import com.raizlabs.android.dbflow.structure.ModelAdapter -import com.raizlabs.android.dbflow.structure.ModelViewAdapter -import com.raizlabs.android.dbflow.structure.QueryModelAdapter -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper -import com.raizlabs.android.dbflow.structure.database.transaction.FastStoreModelTransaction.* -import com.raizlabs.android.dbflow.structure.database.transaction.ProcessModelTransaction -import com.raizlabs.android.dbflow.structure.database.transaction.ProcessModelTransaction.ProcessModel -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction - -typealias ProcessFunction = (T, DatabaseWrapper) -> Unit - -/** - * Easily get access to its [DatabaseDefinition] directly. - */ -inline fun database() = getDatabase(T::class.java) - -inline fun writableDatabaseForTable() = getWritableDatabaseForTable(T::class.java) - -/** - * Easily get access to its [DatabaseDefinition] directly. - */ -inline fun databaseForTable() = getDatabaseForTable(T::class.java) - -/** - * Easily get its table name. - */ -inline fun tableName() = getTableName(T::class.java) - -/** - * Easily get its [ModelAdapter]. - */ -inline fun modelAdapter() = getModelAdapter(T::class.java) - -/** - * Easily get its [QueryModelAdapter]. - */ -inline fun queryModelAdapter() = getQueryModelAdapter(T::class.java) - -/** - * Easily get its [ModelViewAdapter] - */ -inline fun modelViewAdapter() = getModelViewAdapter(T::class.java) - -/** - * Enables a collection of T objects to easily operate on them within a synchronous database transaction. - */ -inline fun Collection.processInTransaction( - crossinline processFunction: ProcessFunction) { - databaseForTable().executeTransaction { db -> forEach { processFunction(it, db) } } -} - -/** - * Places the [Collection] of items on the [ITransactionQueue]. Use the [processFunction] to perform - * an action on each individual [Model]. This happens on a non-UI thread. - */ -inline fun Collection.processInTransactionAsync( - crossinline processFunction: ProcessFunction, - success: Transaction.Success? = null, - error: Transaction.Error? = null, - processListener: ProcessModelTransaction.OnModelProcessListener? = null) { - databaseForTable().beginTransactionAsync( - this.processTransaction(processFunction) - .processListener(processListener).build()) - .success(success).error(error).execute() -} - -inline fun Collection.processTransaction( - crossinline processFunction: ProcessFunction): ProcessModelTransaction.Builder { - return ProcessModelTransaction.Builder(ProcessModel { model, wrapper -> processFunction(model, wrapper) }).addAll(this) -} - -inline fun Collection.fastSave() = saveBuilder(modelAdapter()).addAll(this) - -inline fun Collection.fastInsert() = insertBuilder(modelAdapter()).addAll(this) - -inline fun Collection.fastUpdate() = updateBuilder(modelAdapter()).addAll(this) - -operator fun ContentValues.set(key: String, value: String?) = put(key, value) - -operator fun ContentValues.set(key: String, value: Byte?) = put(key, value) - -operator fun ContentValues.set(key: String, value: Short?) = put(key, value) - -operator fun ContentValues.set(key: String, value: Int?) = put(key, value) - -operator fun ContentValues.set(key: String, value: Long?) = put(key, value) - -operator fun ContentValues.set(key: String, value: Float?) = put(key, value) - -operator fun ContentValues.set(key: String, value: Double?) = put(key, value) - -operator fun ContentValues.set(key: String, value: Boolean?) = put(key, value) - -operator fun ContentValues.set(key: String, value: ByteArray?) = put(key, value) - - - diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/FlowListExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/FlowListExtensions.kt deleted file mode 100644 index 406affaea..000000000 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/FlowListExtensions.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.raizlabs.android.dbflow.kotlinextensions - -import com.raizlabs.android.dbflow.list.IFlowCursorIterator - -operator fun IFlowCursorIterator.get(i: Long): TModel = this.getItem(i) ?: throw IndexOutOfBoundsException("Could not find item at index $i from the cursor.") - -operator fun IFlowCursorIterator.get(i: Int): TModel = this.getItem(i.toLong()) ?: throw IndexOutOfBoundsException("Could not find item at index $i from the cursor.") - - diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/ModelExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/ModelExtensions.kt deleted file mode 100644 index d136fe9b5..000000000 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/ModelExtensions.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.raizlabs.android.dbflow.kotlinextensions - -import com.raizlabs.android.dbflow.structure.AsyncModel -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper - -inline fun T.save(databaseWrapper: DatabaseWrapper = writableDatabaseForTable()) = modelAdapter().save(this, databaseWrapper) - -inline fun T.insert(databaseWrapper: DatabaseWrapper = writableDatabaseForTable()) = modelAdapter().insert(this, databaseWrapper) - -inline fun T.update(databaseWrapper: DatabaseWrapper = writableDatabaseForTable()) = modelAdapter().update(this, databaseWrapper) - -inline fun T.delete(databaseWrapper: DatabaseWrapper = writableDatabaseForTable()) = modelAdapter().delete(this, databaseWrapper) - -inline fun T.exists(databaseWrapper: DatabaseWrapper = writableDatabaseForTable()) = modelAdapter().exists(this, databaseWrapper) - -inline fun T.async() = AsyncModel(this) \ No newline at end of file diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/NameAliasExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/NameAliasExtensions.kt deleted file mode 100644 index 915e4917b..000000000 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/NameAliasExtensions.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.raizlabs.android.dbflow.kotlinextensions - -import com.raizlabs.android.dbflow.sql.language.NameAlias - -val String.nameAlias - get() = NameAlias.of(this) - -fun String.`as`(alias: String? = null) = NameAlias.of(this, alias) - diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/OperatorExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/OperatorExtensions.kt deleted file mode 100644 index aeadca011..000000000 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/OperatorExtensions.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.raizlabs.android.dbflow.kotlinextensions - -import com.raizlabs.android.dbflow.annotation.Collate -import com.raizlabs.android.dbflow.sql.language.NameAlias -import com.raizlabs.android.dbflow.sql.language.Operator -import com.raizlabs.android.dbflow.sql.language.OperatorGroup -import com.raizlabs.android.dbflow.sql.language.OperatorGroup.clause -import com.raizlabs.android.dbflow.sql.language.SQLOperator - -fun SQLOperator.clause() = OperatorGroup.clause(this) - -fun NameAlias.op() = Operator.op(this) - -fun String.op(): Operator = nameAlias.op() - -infix fun Operator.collate(collation: Collate) = collate(collation) - -infix fun Operator.collate(collation: String) = collate(collation) - -infix fun Operator.postfix(collation: String) = postfix(collation) - -infix fun Operator.Between.and(value: T?) = and(value) - -infix fun Operator.In.and(value: T?) = and(value) - -infix fun Operator.and(sqlOperator: SQLOperator) = clause(this).and(sqlOperator) - -infix fun Operator.or(sqlOperator: SQLOperator) = clause(this).or(sqlOperator) - -infix fun Operator.andAll(sqlOperator: Collection) = clause(this).andAll(sqlOperator) - -infix fun Operator.orAll(sqlOperator: Collection) = clause(this).orAll(sqlOperator) - -infix fun OperatorGroup.and(sqlOperator: SQLOperator) = and(sqlOperator) - -infix fun OperatorGroup.or(sqlOperator: SQLOperator) = or(sqlOperator) - -infix fun OperatorGroup.and(sqlOperator: OperatorGroup) = clause().and(sqlOperator) - -infix fun OperatorGroup.or(sqlOperator: OperatorGroup) = clause().or(sqlOperator) diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyExtensions.kt deleted file mode 100644 index 59b4f4162..000000000 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyExtensions.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.raizlabs.android.dbflow.kotlinextensions - -import com.raizlabs.android.dbflow.sql.language.property.Property -import com.raizlabs.android.dbflow.sql.language.property.PropertyFactory.from -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable -import kotlin.reflect.KClass - -/** - * Description: Provides some very nice Property class extensions. - */ - -val Int.property - get() = from(this) - -val Char.property - get() = from(this) - -val Double.property - get() = from(this) - -val Long.property - get() = from(this) - -val Float.property - get() = from(this) - -val Short.property - get() = from(this) - -val Byte.property - get() = from(this) - -val T?.property - get() = from(this) - -val ModelQueriable.property - get() = from(this) - -inline fun propertyString(stringRepresentation: String?) = from(T::class.java, stringRepresentation) - -inline fun KClass.allProperty() = Property.allProperty(this.java) \ No newline at end of file diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt deleted file mode 100644 index 31b3d8bff..000000000 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.raizlabs.android.dbflow.kotlinextensions - -import com.raizlabs.android.dbflow.sql.language.BaseModelQueriable -import com.raizlabs.android.dbflow.sql.language.IConditional -import com.raizlabs.android.dbflow.sql.language.Operator -import com.raizlabs.android.dbflow.sql.language.Operator.Between -import com.raizlabs.android.dbflow.sql.language.property.Property - -/** - * Description: Provides property methods in via infix functions. - */ - -infix fun Property.eq(value: T?) = this.eq(value) - -infix fun Property.`is`(value: T?) = this.`is`(value) - -infix fun Property.isNot(value: T?) = this.isNot(value) - -infix fun Property.notEq(value: T?) = this.notEq(value) - -infix fun Property.like(value: String) = this.like(value) - -infix fun Property.glob(value: String) = this.glob(value) - -infix fun Property.greaterThan(value: T) = this.greaterThan(value) - -infix fun Property.greaterThanOrEq(value: T) = this.greaterThanOrEq(value) - -infix fun Property.lessThan(value: T) = this.lessThan(value) - -infix fun Property.lessThanOrEq(value: T) = this.lessThanOrEq(value) - -infix fun Property.between(value: T) = this.between(value) - -infix fun Property.`in`(values: Array): Operator.In { - return when (values.size) { - 1 -> `in`(values[0]) - else -> this.`in`(values[0], *values.sliceArray(IntRange(1, values.size))) - } -} - -infix fun Property.notIn(values: Array): Operator.In { - return when (values.size) { - 1 -> notIn(values[0]) - else -> this.notIn(values[0], *values.sliceArray(IntRange(1, values.size))) - } -} - -infix fun Property.`in`(values: Collection) = this.`in`(values) - -infix fun Property.notIn(values: Collection) = this.notIn(values) - -infix fun Property.concatenate(value: T) = this.concatenate(value) - -infix fun IConditional.eq(value: IConditional): Operator<*> = this.eq(value) - -infix fun IConditional.`is`(conditional: IConditional): Operator<*> = this.`is`(conditional) - -infix fun IConditional.isNot(conditional: IConditional): Operator<*> = this.isNot(conditional) - -infix fun IConditional.notEq(conditional: IConditional): Operator<*> = this.notEq(conditional) - -infix fun IConditional.like(conditional: IConditional): Operator<*> = this.like(conditional) - -infix fun IConditional.glob(conditional: IConditional): Operator<*> = this.glob(conditional) - -infix fun IConditional.like(value: String): Operator<*> = this.like(value) - -infix fun IConditional.glob(value: String): Operator<*> = this.glob(value) - -infix fun IConditional.greaterThan(conditional: IConditional): Operator<*> = this.greaterThan(conditional) - -infix fun IConditional.greaterThanOrEq(conditional: IConditional): Operator<*> = this.greaterThanOrEq(conditional) - -infix fun IConditional.lessThan(conditional: IConditional): Operator<*> = this.lessThan(conditional) - -infix fun IConditional.lessThanOrEq(conditional: IConditional): Operator<*> = this.lessThanOrEq(conditional) - -infix fun IConditional.between(conditional: IConditional): Between<*> = this.between(conditional) - -infix fun IConditional.`in`(values: Array): Operator.In<*> { - return when (values.size) { - 1 -> `in`(values[0]) - else -> this.`in`(values[0], *values.sliceArray(IntRange(1, values.size))) - } -} - -infix fun IConditional.notIn(values: Array): Operator.In<*> { - return when (values.size) { - 1 -> notIn(values[0]) - else -> this.notIn(values[0], *values.sliceArray(IntRange(1, values.size))) - } -} - -infix fun IConditional.`is`(baseModelQueriable: BaseModelQueriable): Operator<*> = this.`is`(baseModelQueriable) - -infix fun IConditional.eq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.eq(baseModelQueriable) - -infix fun IConditional.isNot(baseModelQueriable: BaseModelQueriable): Operator<*> = this.isNot(baseModelQueriable) -infix fun IConditional.notEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.notEq(baseModelQueriable) -infix fun IConditional.like(baseModelQueriable: BaseModelQueriable): Operator<*> = this.like(baseModelQueriable) -infix fun IConditional.glob(baseModelQueriable: BaseModelQueriable): Operator<*> = this.glob(baseModelQueriable) -infix fun IConditional.greaterThan(baseModelQueriable: BaseModelQueriable): Operator<*> = this.greaterThan(baseModelQueriable) -infix fun IConditional.greaterThanOrEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.greaterThanOrEq(baseModelQueriable) -infix fun IConditional.lessThan(baseModelQueriable: BaseModelQueriable): Operator<*> = this.lessThan(baseModelQueriable) -infix fun IConditional.lessThanOrEq(baseModelQueriable: BaseModelQueriable): Operator<*> = this.lessThanOrEq(baseModelQueriable) -infix fun IConditional.between(baseModelQueriable: BaseModelQueriable): Between<*> = this.between(baseModelQueriable) - -infix fun IConditional.`in`(values: Array>): Operator.In<*> { - return when (values.size) { - 1 -> `in`(values[0]) - else -> this.`in`(values[0], *values.sliceArray(IntRange(1, values.size))) - } -} - -infix fun IConditional.notIn(values: Array>): Operator.In<*> { - return when (values.size) { - 1 -> notIn(values[0]) - else -> this.notIn(values[0], *values.sliceArray(IntRange(1, values.size))) - } -} - -infix fun IConditional.concatenate(conditional: IConditional): Operator<*> = this.concatenate(conditional) - diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt deleted file mode 100644 index a4a84ee10..000000000 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt +++ /dev/null @@ -1,266 +0,0 @@ -package com.raizlabs.android.dbflow.kotlinextensions - -import com.raizlabs.android.dbflow.annotation.Collate -import com.raizlabs.android.dbflow.sql.Query -import com.raizlabs.android.dbflow.sql.language.BaseModelQueriable -import com.raizlabs.android.dbflow.sql.language.Case -import com.raizlabs.android.dbflow.sql.language.CaseCondition -import com.raizlabs.android.dbflow.sql.language.CompletedTrigger -import com.raizlabs.android.dbflow.sql.language.CursorResult -import com.raizlabs.android.dbflow.sql.language.From -import com.raizlabs.android.dbflow.sql.language.Index -import com.raizlabs.android.dbflow.sql.language.Insert -import com.raizlabs.android.dbflow.sql.language.Join -import com.raizlabs.android.dbflow.sql.language.NameAlias -import com.raizlabs.android.dbflow.sql.language.OrderBy -import com.raizlabs.android.dbflow.sql.language.SQLOperator -import com.raizlabs.android.dbflow.sql.language.SQLite -import com.raizlabs.android.dbflow.sql.language.Select -import com.raizlabs.android.dbflow.sql.language.Set -import com.raizlabs.android.dbflow.sql.language.Transformable -import com.raizlabs.android.dbflow.sql.language.Trigger -import com.raizlabs.android.dbflow.sql.language.TriggerMethod -import com.raizlabs.android.dbflow.sql.language.Update -import com.raizlabs.android.dbflow.sql.language.Where -import com.raizlabs.android.dbflow.sql.language.property.IProperty -import com.raizlabs.android.dbflow.sql.queriable.AsyncQuery -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable -import com.raizlabs.android.dbflow.sql.queriable.Queriable -import com.raizlabs.android.dbflow.structure.AsyncModel -import com.raizlabs.android.dbflow.structure.Model -import com.raizlabs.android.dbflow.structure.database.transaction.QueryTransaction -import kotlin.reflect.KClass - -/** - * Description: A file containing extensions for adding query syntactic sugar. - */ - -inline val select: Select - get() = SQLite.select() - -inline fun Select.from() = from(T::class.java) - -fun delete(modelClass: KClass) = SQLite.delete(modelClass.java) - -infix fun Select.from(modelClass: KClass) = from(modelClass.java) - -infix fun From.whereExists(where: Where) = where().exists(where) - -infix fun From.where(sqlOperator: SQLOperator) = where(sqlOperator) - -infix fun From.`as`(alias: String) = `as`(alias) - -infix fun Set.where(sqlOperator: SQLOperator) = where(sqlOperator) - -infix fun Where.and(sqlOperator: SQLOperator) = and(sqlOperator) - -infix fun Where.or(sqlOperator: SQLOperator) = or(sqlOperator) - -infix fun Case.`when`(sqlOperator: SQLOperator) = `when`(sqlOperator) - -infix fun Case.`when`(property: IProperty<*>) = `when`(property) - -infix fun Case.`when`(value: T?) = `when`(value) - -infix fun CaseCondition.then(value: T?) = then(value) - -infix fun CaseCondition.then(property: IProperty<*>) = then(property) - -infix fun Case.`else`(value: T?) = _else(value) - -infix fun Case.end(columnName: String) = end(columnName) - -fun case(caseColumn: IProperty<*>) = SQLite._case(caseColumn) - -fun caseWhen(operator: SQLOperator) = SQLite.caseWhen(operator) - -inline fun insert() = Insert(T::class.java) - -inline fun indexOn(indexName: String, vararg property: IProperty<*>) = Index(indexName).on(T::class.java, *property) - -inline fun indexOn(indexName: String, firstNameAlias: NameAlias, vararg arrayOfNameAlias: NameAlias) = Index(indexName).on(T::class.java, firstNameAlias, *arrayOfNameAlias) - -// queriable extensions - -inline val Queriable.count - get() = count() - -inline val Queriable.cursor - get() = query() - -inline val Queriable.hasData - get() = hasData() - -inline val Queriable.statement - get() = compileStatement() - -inline val ModelQueriable.list - get() = queryList() - -inline val ModelQueriable.result - get() = querySingle() - -inline val ModelQueriable.cursorResult - get() = queryResults() - -inline val ModelQueriable.flowQueryList - get() = flowQueryList() - -inline val ModelQueriable.cursorList - get() = cursorList() - -// cursor result extensions -inline fun CursorResult<*>.toCustomList() = toCustomList(T::class.java) - -inline fun CursorResult<*>.toCustomListClose() = toCustomListClose(T::class.java) - -inline fun CursorResult<*>.toCustomModel() = toCustomModel(T::class.java) - -inline fun CursorResult<*>.toCustomModelClose() = toCustomModelClose(T::class.java) - -// async extensions - -inline val ModelQueriable.async - get() = async() - -infix inline fun AsyncQuery.list(crossinline callback: (QueryTransaction<*>, MutableList) -> Unit) - = queryListResultCallback { queryTransaction, mutableList -> callback(queryTransaction, mutableList) } - .execute() - -infix inline fun AsyncQuery.result(crossinline callback: (QueryTransaction<*>, T?) -> Unit) - = querySingleResultCallback { queryTransaction, model -> callback(queryTransaction, model) } - .execute() - -infix inline fun AsyncQuery.cursorResult(crossinline callback: (QueryTransaction<*>, CursorResult) -> Unit) - = queryResultCallback { queryTransaction, cursorResult -> callback(queryTransaction, cursorResult) } - .execute() - -inline val Model.async - get() = async() - -infix inline fun AsyncModel.insert(crossinline listener: (T) -> Unit) = withListener { listener(it) }.insert() - -infix inline fun AsyncModel.update(crossinline listener: (T) -> Unit) = withListener { listener(it) }.update() - -infix inline fun AsyncModel.delete(crossinline listener: (T) -> Unit) = withListener { listener(it) }.delete() - -infix inline fun AsyncModel.save(crossinline listener: (T) -> Unit) = withListener { listener(it) }.save() - -// Transformable methods - -infix fun Transformable.groupBy(nameAlias: NameAlias): Where = groupBy(nameAlias) - -infix fun Transformable.groupBy(property: IProperty<*>): Where = groupBy(property) - -infix fun Transformable.orderBy(orderBy: OrderBy): Where = orderBy(orderBy) - -infix fun Transformable.limit(limit: Int): Where = limit(limit) - -infix fun Transformable.offset(offset: Int): Where = offset(offset) - -infix fun Transformable.having(sqlOperator: SQLOperator): Where = having(sqlOperator) - -infix fun OrderBy.collate(collate: Collate) = collate(collate) - -// join - -infix fun From.innerJoin(joinTable: KClass): Join = join(joinTable.java, Join.JoinType.INNER) - -infix fun From.crossJoin(joinTable: KClass): Join = join(joinTable.java, Join.JoinType.CROSS) - -infix fun From.leftOuterJoin(joinTable: KClass): Join = join(joinTable.java, Join.JoinType.LEFT_OUTER) - -infix fun From.naturalJoin(joinTable: KClass): Join = join(joinTable.java, Join.JoinType.NATURAL) - -infix fun Join.on(sqlOperator: SQLOperator): From = on(sqlOperator) - -infix fun Join.using(property: IProperty<*>): From = using(property) - -// update methods - -inline fun update() = SQLite.update(T::class.java) - -infix fun Update.set(sqlOperator: SQLOperator) = set(sqlOperator) - -// delete - -inline fun delete() = SQLite.delete(T::class.java) - -inline fun delete(deleteClause: From.() -> BaseModelQueriable) - = deleteClause(SQLite.delete(T::class.java)) - -// insert methods - -fun insert(modelClass: KClass) = SQLite.insert(modelClass.java) - -infix fun Insert.orReplace(into: Array, *>>) = orReplace().columnValues(*into) - -infix fun Insert.orRollback(into: Array, *>>) = orRollback().columnValues(*into) - -infix fun Insert.orAbort(into: Array, *>>) = orAbort().columnValues(*into) - -infix fun Insert.orFail(into: Array, *>>) = orFail().columnValues(*into) - -infix fun Insert.orIgnore(into: Array, *>>) = orIgnore().columnValues(*into) - -infix fun Insert.select(from: From<*>): Insert = select(from) - -fun columnValues(vararg pairs: Pair, *>): Array, *>> = pairs - -fun Insert.columnValues(vararg pairs: Pair, *>): Insert { - val columns: MutableList> = java.util.ArrayList() - val values = java.util.ArrayList() - pairs.forEach { - columns.add(it.first) - values.add(it.second) - } - this.columns(columns).values(values) - return this -} - -// Trigger -fun createTrigger(name: String) = Trigger.create(name) - -infix fun Trigger.deleteOn(kClass: KClass) = deleteOn(kClass.java) - -infix fun Trigger.insertOn(kClass: KClass) = insertOn(kClass.java) - -infix fun Trigger.updateOn(kClass: KClass) = updateOn(kClass.java) - -infix fun TriggerMethod.begin(triggerStatement: Query) = begin(triggerStatement) - -infix fun CompletedTrigger.and(nextStatement: Query) = and(nextStatement) - -// DSL - -fun select(vararg property: IProperty>, - init: Select.() -> BaseModelQueriable): BaseModelQueriable { - val select = SQLite.select(*property) - return init(select) -} - -fun select(init: Select.() -> BaseModelQueriable) = init(SQLite.select()) - -inline fun Select.from(fromClause: From.() -> Where) = fromClause(from(T::class.java)) - -inline fun From.where(sqlOperatorClause: () -> SQLOperator) = where(sqlOperatorClause()) - -inline fun Set.where(sqlOperatorClause: () -> SQLOperator) = where(sqlOperatorClause()) - -inline fun Where.and(sqlOperatorClause: () -> SQLOperator) = and(sqlOperatorClause()) - -inline fun Where.or(sqlOperatorClause: () -> SQLOperator) = or(sqlOperatorClause()) - -inline fun From.join(joinType: Join.JoinType, - function: Join.() -> Unit): Where { - function(join(TJoin::class.java, joinType)) - return where() -} - -inline fun insert(insertMethod: Insert.() -> Unit) = SQLite.insert(T::class.java).apply { insertMethod(this) } - -inline infix fun Join.on(operatorFunction: () -> Array) = on(*operatorFunction()) - -inline fun update(setMethod: Update.() -> BaseModelQueriable) = setMethod(SQLite.update(T::class.java)) - -inline fun Update.set(setClause: Set.() -> Where) = setClause(set()) \ No newline at end of file diff --git a/dbflow-processor/build.gradle b/dbflow-processor/build.gradle deleted file mode 100644 index b78e31608..000000000 --- a/dbflow-processor/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -apply plugin: 'java' -apply plugin: 'kotlin' - -project.ext.artifactId = bt_name - -targetCompatibility = JavaVersion.VERSION_1_8 -sourceCompatibility = JavaVersion.VERSION_1_8 - -dependencies { - compile project("${dbflow_project_prefix}dbflow-core") - compile 'com.squareup:javapoet:1.9.0' - compile 'com.github.agrosner:KPoet:1.0.0' - compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" - - compileOnly 'org.glassfish:javax.annotation:10.0-b28' - - testImplementation 'junit:junit:4.12' - -} - -apply from: '../java-artifacts.gradle' diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt deleted file mode 100644 index 36b4bf4bb..000000000 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/ProcessorUtils.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.raizlabs.android.dbflow.processor - -import com.raizlabs.android.dbflow.processor.ProcessorManager.Companion.manager -import com.raizlabs.android.dbflow.processor.utils.ElementUtility -import com.raizlabs.android.dbflow.processor.utils.erasure -import com.raizlabs.android.dbflow.processor.utils.toTypeElement -import com.squareup.javapoet.ClassName -import javax.annotation.processing.ProcessingEnvironment -import javax.lang.model.element.Element -import javax.lang.model.element.Modifier -import javax.lang.model.element.TypeElement -import javax.lang.model.type.TypeMirror -import javax.tools.Diagnostic - -/** - * Description: Provides handy methods for processing - */ -object ProcessorUtils { - - /** - * Whether the specified element is assignable to the fqTn parameter - - * @param processingEnvironment The environment this runs in - * * - * @param fqTn THe fully qualified type name of the element we want to check - * * - * @param element The element to check that implements - * * - * @return true if element implements the fqTn - */ - fun implementsClass(processingEnvironment: ProcessingEnvironment, fqTn: String, element: TypeElement?): Boolean { - val typeElement = processingEnvironment.elementUtils.getTypeElement(fqTn) - if (typeElement == null) { - processingEnvironment.messager.printMessage(Diagnostic.Kind.ERROR, - "Type Element was null for: $fqTn ensure that the visibility of the class is not private.") - return false - } else { - val classMirror: TypeMirror? = typeElement.asType().erasure() - if (classMirror == null || element?.asType() == null) { - return false - } - val elementType = element.asType() - return elementType != null && (processingEnvironment.typeUtils.isAssignable(elementType, classMirror) || elementType == classMirror) - } - } - - /** - * Whether the specified element is assignable to the fqTn parameter - - * @param processingEnvironment The environment this runs in - * * - * @param fqTn THe fully qualified type name of the element we want to check - * * - * @param element The element to check that implements - * * - * @return true if element implements the fqTn - */ - fun isSubclass(processingEnvironment: ProcessingEnvironment, fqTn: String, element: TypeElement?): Boolean { - val typeElement = processingEnvironment.elementUtils.getTypeElement(fqTn) - if (typeElement == null) { - processingEnvironment.messager.printMessage(Diagnostic.Kind.ERROR, "Type Element was null for: $fqTn ensure that the visibility of the class is not private.") - return false - } else { - val classMirror = typeElement.asType() - return classMirror != null && element != null && element.asType() != null && processingEnvironment.typeUtils.isSubtype(element.asType(), classMirror) - } - } - - fun fromTypeMirror(typeMirror: TypeMirror, processorManager: ProcessorManager): ClassName? { - val element = getTypeElement(typeMirror) - return if (element != null) { - ClassName.get(element) - } else { - ElementUtility.getClassName(typeMirror.toString(), processorManager) - } - } - - fun getTypeElement(element: Element): TypeElement? { - val typeElement: TypeElement? - if (element is TypeElement) { - typeElement = element - } else { - typeElement = getTypeElement(element.asType()) - } - return typeElement - } - - fun getTypeElement(typeMirror: TypeMirror): TypeElement? { - val manager = ProcessorManager.manager - var typeElement: TypeElement? = typeMirror.toTypeElement(manager) - if (typeElement == null) { - val el = manager.typeUtils.asElement(typeMirror) - typeElement = if (el != null) (el as TypeElement) else null - } - return typeElement - } - - fun ensureVisibleStatic(element: Element, typeElement: TypeElement, - name: String) { - if (element.modifiers.contains(Modifier.PRIVATE) - || element.modifiers.contains(Modifier.PROTECTED)) { - manager.logError("$name must be visible from: " + typeElement) - } - if (!element.modifiers.contains(Modifier.STATIC)) { - manager.logError("$name must be static from: " + typeElement) - } - - if (!element.modifiers.contains(Modifier.FINAL)) { - manager.logError("The $name must be final") - } - } -} diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt deleted file mode 100644 index 5df837639..000000000 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/InternalAdapterHelper.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.raizlabs.android.dbflow.processor.definition - -import com.raizlabs.android.dbflow.processor.ClassNames -import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition -import com.raizlabs.android.dbflow.processor.definition.column.DefinitionUtils -import com.raizlabs.android.dbflow.processor.utils.ModelUtils -import com.raizlabs.android.dbflow.sql.QueryBuilder -import com.squareup.javapoet.ArrayTypeName -import com.squareup.javapoet.ClassName -import com.squareup.javapoet.MethodSpec -import com.squareup.javapoet.ParameterizedTypeName -import com.squareup.javapoet.TypeName -import com.squareup.javapoet.TypeSpec -import javax.lang.model.element.Modifier - -/** - * Description: Assists in writing methods for adapters - */ -object InternalAdapterHelper { - - fun writeGetModelClass(typeBuilder: TypeSpec.Builder, modelClassName: ClassName?) { - typeBuilder.addMethod(MethodSpec.methodBuilder("getModelClass") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addStatement("return \$T.class", modelClassName) - .returns(ParameterizedTypeName.get(ClassName.get(Class::class.java), modelClassName)) - .build()) - } - - fun writeGetTableName(typeBuilder: TypeSpec.Builder, tableName: String?) { - typeBuilder.addMethod(MethodSpec.methodBuilder("getTableName") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addStatement("return \$S", QueryBuilder.quote(tableName)) - .returns(ClassName.get(String::class.java)) - .build()) - } - - fun writeUpdateAutoIncrement(typeBuilder: TypeSpec.Builder, modelClassName: TypeName?, - autoIncrementDefinition: ColumnDefinition) { - typeBuilder.addMethod(MethodSpec.methodBuilder("updateAutoIncrement") - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(modelClassName, ModelUtils.variable) - .addParameter(ClassName.get(Number::class.java), "id") - .addCode(autoIncrementDefinition.updateAutoIncrementMethod) - .build()) - } - - fun writeGetCachingId(typeBuilder: TypeSpec.Builder, modelClassName: TypeName?, - primaryColumns: List) { - if (primaryColumns.size > 1) { - var methodBuilder: MethodSpec.Builder = MethodSpec.methodBuilder("getCachingColumnValuesFromModel") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ArrayTypeName.of(Any::class.java), "inValues") - .addParameter(modelClassName, ModelUtils.variable) - for (i in primaryColumns.indices) { - val column = primaryColumns[i] - methodBuilder.addCode(column.getColumnAccessString(i)) - } - methodBuilder.addStatement("return \$L", "inValues").returns(ArrayTypeName.of(Any::class.java)) - typeBuilder.addMethod(methodBuilder.build()) - - methodBuilder = MethodSpec.methodBuilder("getCachingColumnValuesFromCursor") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ArrayTypeName.of(Any::class.java), "inValues") - .addParameter(ClassNames.CURSOR, "cursor") - for (i in primaryColumns.indices) { - val column = primaryColumns[i] - val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) - methodBuilder.addStatement("inValues[\$L] = \$L.\$L(\$L.getColumnIndex(\$S))", i, LoadFromCursorMethod.PARAM_CURSOR, - method, LoadFromCursorMethod.PARAM_CURSOR, column.columnName) - } - methodBuilder.addStatement("return \$L", "inValues").returns(ArrayTypeName.of(Any::class.java)) - typeBuilder.addMethod(methodBuilder.build()) - } else { - // single primary key - var methodBuilder: MethodSpec.Builder = MethodSpec.methodBuilder("getCachingColumnValueFromModel") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(modelClassName, ModelUtils.variable) - methodBuilder.addCode(primaryColumns[0].getSimpleAccessString()) - .returns(Any::class.java) - typeBuilder.addMethod(methodBuilder.build()) - - methodBuilder = MethodSpec.methodBuilder("getCachingColumnValueFromCursor") - .addAnnotation(Override::class.java).addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(ClassNames.CURSOR, "cursor") - val column = primaryColumns[0] - val method = DefinitionUtils.getLoadFromCursorMethodString(column.elementTypeName, column.wrapperTypeName) - methodBuilder.addStatement("return \$L.\$L(\$L.getColumnIndex(\$S))", LoadFromCursorMethod.PARAM_CURSOR, - method, LoadFromCursorMethod.PARAM_CURSOR, column.columnName).returns(Any::class.java) - typeBuilder.addMethod(methodBuilder.build()) - - methodBuilder = MethodSpec.methodBuilder("getCachingId") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .addParameter(modelClassName, ModelUtils.variable) - .addStatement("return getCachingColumnValueFromModel(\$L)", - ModelUtils.variable).returns(TypeName.OBJECT) - typeBuilder.addMethod(methodBuilder.build()) - } - } - -} diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/UniqueGroupsDefinition.kt b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/UniqueGroupsDefinition.kt deleted file mode 100644 index eebeffd9a..000000000 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/definition/UniqueGroupsDefinition.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.raizlabs.android.dbflow.processor.definition - -import com.raizlabs.android.dbflow.annotation.ConflictAction -import com.raizlabs.android.dbflow.annotation.UniqueGroup -import com.raizlabs.android.dbflow.processor.definition.column.ColumnDefinition -import com.raizlabs.android.dbflow.processor.definition.column.ReferenceColumnDefinition -import com.raizlabs.android.dbflow.sql.QueryBuilder -import com.squareup.javapoet.CodeBlock -import java.util.* - -/** - * Description: - */ -class UniqueGroupsDefinition(uniqueGroup: UniqueGroup) { - - var columnDefinitionList: MutableList = ArrayList() - - var number: Int = uniqueGroup.groupNumber - - private val uniqueConflict: ConflictAction = uniqueGroup.uniqueConflict - - fun addColumnDefinition(columnDefinition: ColumnDefinition) { - columnDefinitionList.add(columnDefinition) - } - - val creationName: CodeBlock - get() { - val codeBuilder = CodeBlock.builder().add(", UNIQUE(") - var count = 0 - columnDefinitionList.forEach { - if (count > 0) { - codeBuilder.add(",") - } - if (it is ReferenceColumnDefinition) { - for (reference in it._referenceDefinitionList) { - codeBuilder.add(QueryBuilder.quote(reference.columnName)) - } - } else { - codeBuilder.add(QueryBuilder.quote(it.columnName)) - } - count++ - } - codeBuilder.add(") ON CONFLICT \$L", uniqueConflict) - return codeBuilder.build() - } -} \ No newline at end of file diff --git a/dbflow-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/dbflow-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index 0d2dfaf75..000000000 --- a/dbflow-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1 +0,0 @@ -com.raizlabs.android.dbflow.processor.DBFlowProcessor \ No newline at end of file diff --git a/dbflow-rx-kotlinextensions/build.gradle b/dbflow-rx-kotlinextensions/build.gradle deleted file mode 100644 index 225aeaadc..000000000 --- a/dbflow-rx-kotlinextensions/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -project.ext.artifactId = bt_name - -android { - compileSdkVersion Integer.valueOf(dbflow_target_sdk) - buildToolsVersion dbflow_build_tools_version - - defaultConfig { - minSdkVersion dbflow_min_sdk_rx - targetSdkVersion Integer.valueOf(dbflow_target_sdk) - } -} - -dependencies { - api project("${dbflow_project_prefix}dbflow") - api project("${dbflow_project_prefix}dbflow-rx") - api "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" -} - -apply from: '../kotlin-artifacts.gradle' diff --git a/dbflow-rx-kotlinextensions/gradle.properties b/dbflow-rx-kotlinextensions/gradle.properties deleted file mode 100644 index c06522005..000000000 --- a/dbflow-rx-kotlinextensions/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -bt_name=dbflow-rx-kotlinextensions -bt_packaging=aar -bt_artifact_id=dbflow-rx-kotlinextensions diff --git a/dbflow-rx-kotlinextensions/src/main/AndroidManifest.xml b/dbflow-rx-kotlinextensions/src/main/AndroidManifest.xml deleted file mode 100644 index 903cee5f9..000000000 --- a/dbflow-rx-kotlinextensions/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/dbflow-rx-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/rx/kotlinextensions/QueryExtensions.kt b/dbflow-rx-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/rx/kotlinextensions/QueryExtensions.kt deleted file mode 100644 index bf3591ffb..000000000 --- a/dbflow-rx-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/rx/kotlinextensions/QueryExtensions.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.raizlabs.android.dbflow.rx.kotlinextensions - -import android.database.Cursor -import com.raizlabs.android.dbflow.rx.language.RXModelQueriable -import com.raizlabs.android.dbflow.rx.language.RXQueriable -import com.raizlabs.android.dbflow.rx.language.RXSQLite -import com.raizlabs.android.dbflow.rx.structure.BaseRXModel -import com.raizlabs.android.dbflow.sql.language.BaseQueriable -import com.raizlabs.android.dbflow.sql.language.CursorResult -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable -import com.raizlabs.android.dbflow.sql.queriable.Queriable -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper -import rx.Subscription - -fun ModelQueriable.rx() = RXSQLite.rx(this) - -fun BaseQueriable.rxBaseQueriable() = RXSQLite.rx(table, this) - -inline fun Queriable.rx() = RXSQLite.rx(T::class.java, this) - -// queriable extensions - -inline val RXQueriable.count - get() = count() - -inline val RXQueriable.cursor - get() = query() - -inline val RXQueriable.hasData - get() = hasData() - -inline val RXQueriable.statement - get() = compileStatement() - -inline val RXModelQueriable.list - get() = queryList() - -inline val RXModelQueriable.result - get() = querySingle() - -inline val RXModelQueriable.cursorResult - get() = queryResults() - -inline val RXModelQueriable.streamResults - get() = queryStreamResults() - -inline val RXModelQueriable.tableChanges - get() = observeOnTableChanges() - -// model extensions - -fun BaseRXModel.save(databaseWrapper: DatabaseWrapper, func: (Boolean) -> Unit): Subscription = save(databaseWrapper).subscribe { func(it) } - -infix inline fun BaseRXModel.save(crossinline func: (Boolean) -> Unit): Subscription = save().subscribe { func(it) } - -fun BaseRXModel.insert(databaseWrapper: DatabaseWrapper, func: (Long) -> Unit): Subscription = insert(databaseWrapper).subscribe { func(it) } - -infix inline fun BaseRXModel.insert(crossinline func: (Long) -> Unit): Subscription = insert().subscribe { func(it) } - -fun BaseRXModel.update(databaseWrapper: DatabaseWrapper, func: (Boolean) -> Unit): Subscription = update(databaseWrapper).subscribe { func(it) } - -infix inline fun BaseRXModel.update(crossinline func: (Boolean) -> Unit): Subscription = update().subscribe { func(it) } - -fun BaseRXModel.delete(databaseWrapper: DatabaseWrapper, func: (Boolean) -> Unit): Subscription = delete(databaseWrapper).subscribe { func(it) } - -infix inline fun BaseRXModel.delete(crossinline func: (Boolean) -> Unit): Subscription = delete().subscribe { func(it) } - -// async extensions - -infix inline fun RXModelQueriable.list(crossinline func: (MutableList) -> Unit): Subscription = list.subscribe { func(it) } - -infix inline fun RXModelQueriable.result(crossinline func: (T?) -> Unit): Subscription = result.subscribe { func(it) } - -infix inline fun RXModelQueriable.streamResults(crossinline func: (T?) -> Unit): Subscription = streamResults.subscribe { func(it) } - -infix inline fun RXModelQueriable.cursorResult(crossinline func: (CursorResult) -> Unit): Subscription = cursorResult.subscribe { func(it) } - -infix inline fun RXModelQueriable.tableChanges(crossinline func: (ModelQueriable) -> Unit): Subscription = tableChanges.subscribe { func(it) } - -infix inline fun RXQueriable.statement(crossinline func: (DatabaseStatement) -> Unit): Subscription = statement.subscribe { func(it) } - -infix inline fun RXQueriable.hasData(crossinline func: (Boolean) -> Unit): Subscription = hasData.subscribe { func(it) } - -infix inline fun RXQueriable.cursor(crossinline func: (Cursor) -> Unit): Subscription = cursor.subscribe { func(it) } - -infix inline fun RXQueriable.count(crossinline func: (Long) -> Unit): Subscription = count.subscribe { func(it) } \ No newline at end of file diff --git a/dbflow-rx/build.gradle b/dbflow-rx/build.gradle deleted file mode 100644 index ef9aeeb3a..000000000 --- a/dbflow-rx/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -apply plugin: 'com.android.library' - -project.ext.artifactId = bt_name - -android { - compileSdkVersion Integer.valueOf(dbflow_target_sdk) - buildToolsVersion dbflow_build_tools_version - - defaultConfig { - minSdkVersion dbflow_min_sdk_rx - targetSdkVersion Integer.valueOf(dbflow_target_sdk) - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } -} - -dependencies { - api project("${dbflow_project_prefix}dbflow") - api 'io.reactivex:rxjava:1.2.7' -} - -apply from: '../android-artifacts.gradle' diff --git a/dbflow-rx/gradle.properties b/dbflow-rx/gradle.properties deleted file mode 100644 index 0dd3c450e..000000000 --- a/dbflow-rx/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -bt_name=dbflow-rx -bt_packaging=aar -bt_artifact_id=dbflow-rx diff --git a/dbflow-rx/src/main/AndroidManifest.xml b/dbflow-rx/src/main/AndroidManifest.xml deleted file mode 100644 index d05f0ea55..000000000 --- a/dbflow-rx/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/CursorResultSubscriber.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/CursorResultSubscriber.java deleted file mode 100644 index db0f5fd63..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/CursorResultSubscriber.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.list.FlowCursorIterator; -import com.raizlabs.android.dbflow.sql.language.CursorResult; - -import java.util.concurrent.atomic.AtomicLong; - -import rx.Observable; -import rx.Producer; -import rx.Subscriber; -import rx.functions.Action1; -import rx.internal.operators.BackpressureUtils; - -/** - * Description: Wraps a {@link RXModelQueriable} into a {@link Observable.OnSubscribe} - * for each element represented by the query. - */ -public class CursorResultSubscriber implements Observable.OnSubscribe { - - @NonNull - private final RXModelQueriable modelQueriable; - - public CursorResultSubscriber(@NonNull RXModelQueriable modelQueriable) { - this.modelQueriable = modelQueriable; - } - - @Override - public void call(Subscriber subscriber) { - subscriber.setProducer(new ElementProducer(subscriber)); - } - - private class ElementProducer implements Producer { - - private final Subscriber subscriber; - private final AtomicLong emitted; - private final AtomicLong requested; - - ElementProducer(Subscriber subscriber) { - this.subscriber = subscriber; - requested = new AtomicLong(); - emitted = new AtomicLong(); - } - - @Override - public void request(final long n) { - if (n == Long.MAX_VALUE && requested.compareAndSet(0, Long.MAX_VALUE) - || n > 0 && BackpressureUtils.getAndAddRequest(requested, n) == 0) { - // emitting all elements - modelQueriable.queryResults().subscribe(new CursorResultAction(n)); - } - } - - private class CursorResultAction implements Action1> { - - private final long limit; - - private CursorResultAction(long limit) { - this.limit = limit; - } - - @Override - public void call(CursorResult ts) { - final int starting = limit == Long.MAX_VALUE && requested.compareAndSet(0, Long.MAX_VALUE) - ? 0 : emitted.intValue(); - long limit = this.limit + starting; - - while (limit > 0) { - FlowCursorIterator iterator = ts.iterator(starting, limit); - try { - long i = 0; - while (!subscriber.isUnsubscribed() && iterator.hasNext() && i++ < limit) { - subscriber.onNext(iterator.next()); - } - emitted.addAndGet(i); - // no more items - if (!subscriber.isUnsubscribed() && i < limit) { - subscriber.onCompleted(); - break; - } - limit = requested.addAndGet(-limit); - } catch (Exception e) { - FlowLog.logError(e); - subscriber.onError(e); - } finally { - try { - iterator.close(); - } catch (Exception e) { - FlowLog.logError(e); - subscriber.onError(e); - } - } - } - } - } - } -} diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXModelQueriable.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXModelQueriable.java deleted file mode 100644 index e168ec708..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXModelQueriable.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.list.FlowCursorList; -import com.raizlabs.android.dbflow.list.FlowQueryList; -import com.raizlabs.android.dbflow.sql.language.CursorResult; -import com.raizlabs.android.dbflow.sql.language.Join; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.BaseQueryModel; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.List; - -import rx.Observable; -import rx.Single; - -/** - * Description: Mirrors {@link ModelQueriable} with RX constructs. - */ -public interface RXModelQueriable extends RXQueriable { - - @NonNull - Single> queryResults(); - - @NonNull - Single> queryList(); - - @NonNull - Single> queryList(DatabaseWrapper wrapper); - - /** - * @return Single model, the first of potentially many results - */ - @NonNull - Single querySingle(); - - /** - * Allows you to specify a DB, useful for migrations. - * - * @return Single model, the first of potentially many results - */ - @NonNull - Single querySingle(DatabaseWrapper wrapper); - - /** - * @return Queries for {@link #queryResults()}, and returns one at a time from this {@link Observable} - */ - @NonNull - Observable queryStreamResults(); - - /** - * @return the table that this query comes from. - */ - @NonNull - Class getTable(); - - /** - * @return A cursor-backed list that handles conversion, retrieval, and caching of lists. Can - * cache models dynamically by setting {@link FlowCursorList#setCacheModels(boolean)} to true. - */ - @NonNull - Single> cursorList(); - - /** - * @return A cursor-backed {@link List} that handles conversion, retrieval, caching, content changes, - * and more. - */ - @NonNull - Single> flowQueryList(); - - /** - * Returns a {@link List} based on the custom {@link TQueryModel} you pass in. - * - * @param queryModelClass The query model class to use. - * @param The class that extends {@link BaseQueryModel} - * @return A list of custom models that are not tied to a table. - */ - @NonNull - Single> queryCustomList(Class queryModelClass); - - /** - * Returns a single {@link TQueryModel} from this query. - * - * @param queryModelClass The class to use. - * @param The class that extends {@link BaseQueryModel} - * @return A single model from the query. - */ - @NonNull - Single queryCustomSingle(Class queryModelClass); - - /** - * Disables caching on this query for the object retrieved from DB (if caching enabled). If - * caching is not enabled, this method is ignored. This also disables caching in a {@link FlowCursorList} - * or {@link FlowQueryList} if you {@link #flowQueryList()} or {@link #cursorList()} - */ - @NonNull - RXModelQueriable disableCaching(); - - /** - * @return A new {@link Observable} that observes when the {@link TModel} table changes. - * This can also be multiple tables, given if it results from a {@link Join} (one for each join table). - */ - @NonNull - Observable> observeOnTableChanges(); -} \ No newline at end of file diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXModelQueriableImpl.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXModelQueriableImpl.java deleted file mode 100644 index c88955951..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXModelQueriableImpl.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.list.FlowCursorList; -import com.raizlabs.android.dbflow.list.FlowQueryList; -import com.raizlabs.android.dbflow.sql.language.BaseModelQueriable; -import com.raizlabs.android.dbflow.sql.language.CursorResult; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.List; -import java.util.concurrent.Callable; - -import rx.Emitter; -import rx.Observable; -import rx.Single; - -import static rx.Single.fromCallable; - -/** - * Description: Represents {@link BaseModelQueriable} in RX form. - */ -public class RXModelQueriableImpl extends RXQueriableImpl implements RXModelQueriable { - - private final ModelQueriable modelQueriable; - - RXModelQueriableImpl(ModelQueriable modelQueriable) { - super(modelQueriable.getTable(), modelQueriable); - this.modelQueriable = modelQueriable; - } - - private ModelQueriable getInnerModelQueriable() { - return modelQueriable; - } - - @NonNull - @Override - public Observable queryStreamResults() { - return Observable.create(new CursorResultSubscriber<>(this)); - } - - @NonNull - @Override - public Single> queryResults() { - return fromCallable(new Callable>() { - @Override - public CursorResult call() throws Exception { - return getInnerModelQueriable().queryResults(); - } - }); - } - - @NonNull - @Override - public Single> queryList() { - return fromCallable(new Callable>() { - @Override - public List call() throws Exception { - return getInnerModelQueriable().queryList(); - } - }); - } - - @NonNull - @Override - public Single> queryList(final DatabaseWrapper wrapper) { - return fromCallable(new Callable>() { - @Override - public List call() throws Exception { - return getInnerModelQueriable().queryList(wrapper); - } - }); - } - - @NonNull - @Override - public Single querySingle() { - return fromCallable(new Callable() { - @Override - public T call() throws Exception { - return getInnerModelQueriable().querySingle(); - } - }); - } - - @NonNull - @Override - public Single querySingle(final DatabaseWrapper wrapper) { - return fromCallable(new Callable() { - @Override - public T call() throws Exception { - return getInnerModelQueriable().querySingle(wrapper); - } - }); - } - - @NonNull - @Override - public Class getTable() { - return getInnerModelQueriable().getTable(); - } - - @NonNull - @Override - public Single> cursorList() { - return fromCallable(new Callable>() { - @Override - public FlowCursorList call() throws Exception { - return getInnerModelQueriable().cursorList(); - } - }); - } - - @NonNull - @Override - public Single> flowQueryList() { - return fromCallable(new Callable>() { - @Override - public FlowQueryList call() throws Exception { - return getInnerModelQueriable().flowQueryList(); - } - }); - } - - @NonNull - @Override - public Single> queryCustomList( - final Class tQueryModelClass) { - return fromCallable(new Callable>() { - @Override - public List call() throws Exception { - return getInnerModelQueriable().queryCustomList(tQueryModelClass); - } - }); - } - - @NonNull - @Override - public Single queryCustomSingle( - final Class tQueryModelClass) { - return fromCallable(new Callable() { - @Override - public TQueryModel call() throws Exception { - return getInnerModelQueriable().queryCustomSingle(tQueryModelClass); - } - }); - } - - @NonNull - @Override - public RXModelQueriable disableCaching() { - getInnerModelQueriable().disableCaching(); - return this; - } - - @NonNull - @Override - public Observable> observeOnTableChanges() { - return Observable.create(new TableChangeListenerEmitter<>(getInnerModelQueriable()), - Emitter.BackpressureMode.LATEST); - } -} diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXQueriable.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXQueriable.java deleted file mode 100644 index 4c86a2e77..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXQueriable.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language; - -import android.database.Cursor; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.language.Delete; -import com.raizlabs.android.dbflow.sql.language.Insert; -import com.raizlabs.android.dbflow.sql.language.Set; -import com.raizlabs.android.dbflow.sql.queriable.Queriable; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import rx.Single; - -/** - * Description: Mirrors {@link Queriable} with RX constructs. - */ -public interface RXQueriable { - - /** - * @return An {@link Single} from the DB based on this query - */ - @NonNull - Single query(); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @param databaseWrapper The wrapper to pass in. - * @return An {@link Single} from the DB based on this query - */ - @NonNull - Single query(DatabaseWrapper databaseWrapper); - - - /** - * @return An {@link Single} of {@link DatabaseStatement} from this query. - */ - @NonNull - Single compileStatement(); - - /** - * @param databaseWrapper The wrapper to use. - * @return An {@link Single} of {@link DatabaseStatement} from this query with database specified. - */ - @NonNull - Single compileStatement(DatabaseWrapper databaseWrapper); - - /** - * @return the count of the results of the query. - * @deprecated use {@link #longValue()} - */ - @NonNull - Single count(); - - /** - * @return the long value of this query. - **/ - @NonNull - Single longValue(); - - /** - * @return the long value of this query. - **/ - @NonNull - Single longValue(DatabaseWrapper databaseWrapper); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @return the count of the results of the query. - * @deprecated use {@link #longValue(DatabaseWrapper)} - */ - @NonNull - Single count(DatabaseWrapper databaseWrapper); - - /** - * @return This may return the number of rows affected from a {@link Insert} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - @NonNull - Single executeInsert(); - - /** - * @return This may return the number of rows affected from a {@link Insert} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - @NonNull - Single executeInsert(DatabaseWrapper databaseWrapper); - - /** - * @return This may return the number of rows affected from a {@link Set} or {@link Delete} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - @NonNull - Single executeUpdateDelete(DatabaseWrapper databaseWrapper); - - /** - * @return This may return the number of rows affected from a {@link Set} or {@link Delete} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - @NonNull - Single executeUpdateDelete(); - - /** - * @return True if this query has data. It will run a {@link #count()} greater than 0. - */ - @NonNull - Single hasData(); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @return True if this query has data. It will run a {@link #count()} greater than 0. - */ - @NonNull - Single hasData(DatabaseWrapper databaseWrapper); - - /** - * Will not return a result, rather simply will execute a SQL statement. Use this for non-SELECT statements or when - * you're not interested in the result. - */ - @NonNull - Single execute(); - - /** - * Will not return a result, rather simply will execute a SQL statement. Use this for non-SELECT statements or when - * you're not interested in the result. - */ - @NonNull - Single execute(DatabaseWrapper databaseWrapper); -} diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXQueriableImpl.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXQueriableImpl.java deleted file mode 100644 index e50e73f12..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXQueriableImpl.java +++ /dev/null @@ -1,212 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language; - -import android.database.Cursor; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.language.BaseQueriable; -import com.raizlabs.android.dbflow.sql.queriable.Queriable; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.concurrent.Callable; - -import rx.Single; - -import static rx.Single.fromCallable; - -/** - * Description: Represents {@link BaseQueriable} with RX constructs. - */ -public class RXQueriableImpl implements RXQueriable { - - private final Class table; - private final Queriable queriable; - - RXQueriableImpl(Class table, Queriable queriable) { - this.table = table; - this.queriable = queriable; - } - - private Queriable getInnerQueriable() { - return queriable; - } - - @NonNull - @Override - public Single query() { - return fromCallable(new Callable() { - @Override - public Cursor call() throws Exception { - return getInnerQueriable().query(); - } - }); - } - - @NonNull - @Override - public Single query(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Cursor call() throws Exception { - return getInnerQueriable().query(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single compileStatement() { - return fromCallable(new Callable() { - @Override - public DatabaseStatement call() throws Exception { - return getInnerQueriable().compileStatement(); - } - }); - } - - @NonNull - @Override - public Single compileStatement(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public DatabaseStatement call() throws Exception { - return getInnerQueriable().compileStatement(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single count() { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().count(); - } - }); - } - - @NonNull - @Override - public Single count(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().count(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single longValue() { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().longValue(); - } - }); - } - - @NonNull - @Override - public Single longValue(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().longValue(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single executeInsert() { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().executeInsert(FlowManager.getWritableDatabaseForTable(table)); - } - }); - } - - @NonNull - @Override - public Single executeInsert(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().executeInsert(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single executeUpdateDelete(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().executeUpdateDelete(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single executeUpdateDelete() { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().executeUpdateDelete(); - } - }); - } - - @NonNull - @Override - public Single hasData() { - return fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return getInnerQueriable().hasData(); - } - }); - } - - @NonNull - @Override - public Single hasData(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return getInnerQueriable().hasData(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single execute() { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - getInnerQueriable().execute(); - return null; - } - }); - } - - @NonNull - @Override - public Single execute(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - getInnerQueriable().execute(databaseWrapper); - return null; - } - }); - } -} diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXSQLite.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXSQLite.java deleted file mode 100644 index aeafc9fbf..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/RXSQLite.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.sql.queriable.Queriable; - -/** - * Description: Main entry point to wrap our queries in RX. - */ -public class RXSQLite { - - @NonNull - public static RXModelQueriableImpl rx(ModelQueriable modelQueriable) { - return new RXModelQueriableImpl<>(modelQueriable); - } - - @NonNull - public static RXQueriableImpl rx(Class table, Queriable queriable) { - return new RXQueriableImpl(table, queriable); - } -} diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/TableChangeListenerEmitter.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/TableChangeListenerEmitter.java deleted file mode 100644 index 3dcc48bd1..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/language/TableChangeListenerEmitter.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.runtime.OnTableChangedListener; -import com.raizlabs.android.dbflow.runtime.TableNotifierRegister; -import com.raizlabs.android.dbflow.sql.language.From; -import com.raizlabs.android.dbflow.sql.language.Join; -import com.raizlabs.android.dbflow.sql.language.Where; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.BaseModel; - -import rx.Emitter; -import rx.Subscription; -import rx.functions.Action1; - -/** - * Description: Emits when table changes occur for the related table on the {@link ModelQueriable}. - * If the {@link ModelQueriable} relates to a {@link Join}, this can be multiple tables. - */ -public class TableChangeListenerEmitter implements Action1>> { - - private final ModelQueriable modelQueriable; - - public TableChangeListenerEmitter(ModelQueriable modelQueriable) { - this.modelQueriable = modelQueriable; - } - - @Override - public void call(Emitter> modelQueriableEmitter) { - modelQueriableEmitter.setSubscription( - new FlowContentObserverSubscription(modelQueriableEmitter, modelQueriable.getTable())); - } - - private class FlowContentObserverSubscription implements Subscription { - - private final Emitter> modelQueriableEmitter; - - private final TableNotifierRegister register; - - private FlowContentObserverSubscription( - Emitter> modelQueriableEmitter, Class table) { - this.modelQueriableEmitter = modelQueriableEmitter; - register = FlowManager.newRegisterForTable(table); - - From from = null; - if (modelQueriable instanceof From) { - from = (From) modelQueriable; - } else if (modelQueriable instanceof Where - && ((Where) modelQueriable).getWhereBase() instanceof From) { - //noinspection unchecked - from = (From) ((Where) modelQueriable).getWhereBase(); - } - - // From could be part of many joins, so we register for all affected tables here. - if (from != null) { - java.util.Set> associatedTables = from.getAssociatedTables(); - for (Class associated : associatedTables) { - register.register(associated); - } - } else { - register.register(table); - } - - register.setListener(onTableChangedListener); - } - - @Override - public void unsubscribe() { - register.unregisterAll(); - } - - @Override - public boolean isUnsubscribed() { - return !register.isSubscribed(); - } - - private final OnTableChangedListener onTableChangedListener - = new OnTableChangedListener() { - @Override - public void onTableChanged(@Nullable Class tableChanged, @NonNull BaseModel.Action action) { - if (modelQueriable.getTable().equals(tableChanged)) { - modelQueriableEmitter.onNext(modelQueriable); - } - } - }; - } -} diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/BaseRXModel.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/BaseRXModel.java deleted file mode 100644 index be2d94817..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/BaseRXModel.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.raizlabs.android.dbflow.rx.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.ColumnIgnore; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.InvalidDBConfiguration; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import rx.Completable; -import rx.Single; - -/** - * Description: Similar to {@link BaseModel} with RX constructs. Extend this for convenience methods. - */ -@SuppressWarnings("unchecked") -public class BaseRXModel { - - @ColumnIgnore - private transient RXModelAdapter modelAdapter; - - @NonNull - public Single save() { - return getRXModelAdapter().save(this); - } - - @NonNull - public Single save(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().save(this, databaseWrapper); - } - - @NonNull - public Completable load() { - return getRXModelAdapter().load(this); - } - - @NonNull - public Completable load(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().load(this, databaseWrapper); - } - - @NonNull - public Single delete() { - return getRXModelAdapter().delete(this); - } - - @NonNull - public Single delete(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().delete(this, databaseWrapper); - } - - @NonNull - public Single update() { - return getRXModelAdapter().update(this); - } - - @NonNull - public Single update(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().update(this, databaseWrapper); - } - - @NonNull - public Single insert() { - return getRXModelAdapter().insert(this); - } - - @NonNull - public Single insert(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().insert(this, databaseWrapper); - } - - @NonNull - public Single exists() { - return getRXModelAdapter().exists(this); - } - - @NonNull - public Single exists(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().exists(this, databaseWrapper); - } - - /** - * @return The associated {@link ModelAdapter}. The {@link FlowManager} - * may throw a {@link InvalidDBConfiguration} for this call if this class - * is not associated with a table, so be careful when using this method. - */ - @NonNull - private RXModelAdapter getRXModelAdapter() { - if (modelAdapter == null) { - modelAdapter = new RXModelAdapter<>(getClass()); - } - return modelAdapter; - } -} diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/RXModelAdapter.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/RXModelAdapter.java deleted file mode 100644 index 0b53d99cb..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/RXModelAdapter.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.raizlabs.android.dbflow.rx.structure; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.Collection; -import java.util.concurrent.Callable; - -import rx.Completable; -import rx.Single; - -/** - * Description: Wraps most {@link ModelAdapter} modification operations into RX-style constructs. - */ -public class RXModelAdapter extends RXRetrievalAdapter { - - public static RXModelAdapter from(ModelAdapter modelAdapter) { - return new RXModelAdapter<>(modelAdapter); - } - - public static RXModelAdapter from(Class table) { - return new RXModelAdapter<>(table); - } - - private final ModelAdapter modelAdapter; - - RXModelAdapter(ModelAdapter modelAdapter) { - super(modelAdapter); - this.modelAdapter = modelAdapter; - } - - public RXModelAdapter(Class table) { - this(FlowManager.getModelAdapter(table)); - } - - public Single save(final T model) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.save(model); - } - }); - } - - public Single save(final T model, final DatabaseWrapper databaseWrapper) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.save(model, databaseWrapper); - } - }); - } - - public Completable saveAll(final Collection models) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.saveAll(models); - return null; - } - }); - } - - public Completable saveAll(final Collection models, final DatabaseWrapper databaseWrapper) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.saveAll(models, databaseWrapper); - return null; - } - }); - } - - public Single insert(final T model) { - return Single.fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return modelAdapter.insert(model); - } - }); - } - - public Single insert(final T model, final DatabaseWrapper databaseWrapper) { - return Single.fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return modelAdapter.insert(model, databaseWrapper); - } - }); - } - - public Completable insertAll(final Collection models) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.insertAll(models); - return null; - } - }); - } - - public Completable insertAll(final Collection models, - final DatabaseWrapper databaseWrapper) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.insertAll(models, databaseWrapper); - return null; - } - }); - } - - public Single update(final T model) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.update(model); - } - }); - } - - public Single update(final T model, final DatabaseWrapper databaseWrapper) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.update(model, databaseWrapper); - } - }); - } - - public Completable updateAll(final Collection models) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.updateAll(models); - return null; - } - }); - } - - public Completable updateAll(final Collection models, final DatabaseWrapper databaseWrapper) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.updateAll(models, databaseWrapper); - return null; - } - }); - } - - public Single delete(final T model) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.delete(model); - } - }); - } - - public Single delete(final T model, final DatabaseWrapper databaseWrapper) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.delete(model, databaseWrapper); - } - }); - } - - public Completable deleteAll(final Collection models) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.deleteAll(models); - return null; - } - }); - } - - public Completable deleteAll(final Collection models, final DatabaseWrapper databaseWrapper) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.deleteAll(models, databaseWrapper); - return null; - } - }); - } -} diff --git a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/RXRetrievalAdapter.java b/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/RXRetrievalAdapter.java deleted file mode 100644 index fba7e7329..000000000 --- a/dbflow-rx/src/main/java/com/raizlabs/android/dbflow/rx/structure/RXRetrievalAdapter.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.raizlabs.android.dbflow.rx.structure; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.RetrievalAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.concurrent.Callable; - -import rx.Completable; -import rx.Single; - -/** - * Description: Mirrors the {@link RetrievalAdapter} with subset of exposed methods, mostly for - * {@link #load(Object)} and {@link #exists(Object)} - */ -public class RXRetrievalAdapter { - - public static RXRetrievalAdapter from(RetrievalAdapter modelAdapter) { - return new RXRetrievalAdapter<>(modelAdapter); - } - - public static RXRetrievalAdapter from(Class table) { - return new RXRetrievalAdapter<>(table); - } - - private final RetrievalAdapter retrievalAdapter; - - RXRetrievalAdapter(RetrievalAdapter retrievalAdapter) { - this.retrievalAdapter = retrievalAdapter; - } - - RXRetrievalAdapter(Class table) { - this(FlowManager.getInstanceAdapter(table)); - } - - public Completable load(final TModel model) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - retrievalAdapter.load(model); - return null; - } - }); - } - - public Completable load(final TModel model, final DatabaseWrapper databaseWrapper) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - retrievalAdapter.load(model, databaseWrapper); - return null; - } - }); - } - - /** - * @param model The model to query values from - * @return True if it exists as a row in the corresponding database table - */ - public Single exists(final TModel model) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return retrievalAdapter.exists(model); - } - }); - } - - public Single exists(final TModel model, final DatabaseWrapper wrapper) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return retrievalAdapter.exists(model, wrapper); - } - }); - } -} diff --git a/dbflow-rx2-kotlinextensions/build.gradle b/dbflow-rx2-kotlinextensions/build.gradle deleted file mode 100644 index 415af0113..000000000 --- a/dbflow-rx2-kotlinextensions/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -project.ext.artifactId = bt_name - -android { - compileSdkVersion Integer.valueOf(dbflow_target_sdk) - buildToolsVersion dbflow_build_tools_version - - defaultConfig { - minSdkVersion dbflow_min_sdk_rx - targetSdkVersion Integer.valueOf(dbflow_target_sdk) - } -} - -dependencies { - api project("${dbflow_project_prefix}dbflow") - api project("${dbflow_project_prefix}dbflow-rx2") - api "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" -} - -apply from: '../kotlin-artifacts.gradle' diff --git a/dbflow-rx2-kotlinextensions/gradle.properties b/dbflow-rx2-kotlinextensions/gradle.properties deleted file mode 100644 index af7922ebc..000000000 --- a/dbflow-rx2-kotlinextensions/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -bt_name=dbflow-rx2-kotlinextensions -bt_packaging=aar -bt_artifact_id=dbflow-rx2-kotlinextensions diff --git a/dbflow-rx2-kotlinextensions/src/main/AndroidManifest.xml b/dbflow-rx2-kotlinextensions/src/main/AndroidManifest.xml deleted file mode 100644 index 816732f3f..000000000 --- a/dbflow-rx2-kotlinextensions/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/dbflow-rx2-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/rx2/kotlinextensions/QueryExtensions.kt b/dbflow-rx2-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/rx2/kotlinextensions/QueryExtensions.kt deleted file mode 100644 index a4d2e3bbb..000000000 --- a/dbflow-rx2-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/rx2/kotlinextensions/QueryExtensions.kt +++ /dev/null @@ -1,84 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.kotlinextensions - -import android.database.Cursor -import com.raizlabs.android.dbflow.rx2.language.RXModelQueriable -import com.raizlabs.android.dbflow.rx2.language.RXQueriable -import com.raizlabs.android.dbflow.rx2.language.RXSQLite -import com.raizlabs.android.dbflow.rx2.structure.BaseRXModel -import com.raizlabs.android.dbflow.sql.language.CursorResult -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable -import com.raizlabs.android.dbflow.sql.queriable.Queriable -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper -import io.reactivex.disposables.Disposable - -fun ModelQueriable.rx() = RXSQLite.rx(this) - -inline fun Queriable.rx() = RXSQLite.rx(T::class.java, this) - -// queriable extensions - -inline val RXQueriable.count - get() = count() - -inline val RXQueriable.cursor - get() = query() - -inline val RXQueriable.hasData - get() = hasData() - -inline val RXQueriable.statement - get() = compileStatement() - -inline val RXModelQueriable.list - get() = queryList() - -inline val RXModelQueriable.result - get() = querySingle() - -inline val RXModelQueriable.cursorResult - get() = queryResults() - -inline val RXModelQueriable.streamResults - get() = queryStreamResults() - -inline val RXModelQueriable.tableChanges - get() = observeOnTableChanges() - -// model extensions - -fun BaseRXModel.save(databaseWrapper: DatabaseWrapper, func: (Boolean) -> Unit): Disposable = save(databaseWrapper).subscribe { success -> func(success) } - -infix inline fun BaseRXModel.save(crossinline func: (Boolean) -> Unit): Disposable = save().subscribe { success -> func(success) } - -fun BaseRXModel.insert(databaseWrapper: DatabaseWrapper, func: (Long) -> Unit): Disposable = insert(databaseWrapper).subscribe { rowId -> func(rowId) } - -infix inline fun BaseRXModel.insert(crossinline func: (Long) -> Unit): Disposable = insert().subscribe { rowId -> func(rowId) } - -fun BaseRXModel.update(databaseWrapper: DatabaseWrapper, func: (Boolean) -> Unit): Disposable = update(databaseWrapper).subscribe { success -> func(success) } - -infix inline fun BaseRXModel.update(crossinline func: (Boolean) -> Unit): Disposable = update().subscribe { success -> func(success) } - -fun BaseRXModel.delete(databaseWrapper: DatabaseWrapper, func: (Boolean) -> Unit): Disposable = delete(databaseWrapper).subscribe { success -> func(success) } - -infix inline fun BaseRXModel.delete(crossinline func: (Boolean) -> Unit): Disposable = delete().subscribe { success -> func(success) } - -// async extensions - -infix inline fun RXModelQueriable.list(crossinline func: (MutableList) -> Unit): Disposable = list.subscribe { modelList -> func(modelList) } - -infix inline fun RXModelQueriable.result(crossinline func: (T?) -> Unit): Disposable = result.subscribe { result -> func(result) } - -infix inline fun RXModelQueriable.streamResults(crossinline func: (T?) -> Unit): Disposable = streamResults.subscribe { model -> func(model) } - -infix inline fun RXModelQueriable.cursorResult(crossinline func: (CursorResult) -> Unit): Disposable = cursorResult.subscribe { result -> func(result) } - -infix inline fun RXModelQueriable.tableChanges(crossinline func: (ModelQueriable) -> Unit): Disposable = tableChanges.subscribe { queriable -> func(queriable) } - -infix inline fun RXQueriable.statement(crossinline func: (DatabaseStatement) -> Unit): Disposable = statement.subscribe { statement -> func(statement) } - -infix inline fun RXQueriable.hasData(crossinline func: (Boolean) -> Unit): Disposable = hasData.subscribe { hasData -> func(hasData) } - -infix inline fun RXQueriable.cursor(crossinline func: (Cursor) -> Unit): Disposable = cursor.subscribe { cursor -> func(cursor) } - -infix inline fun RXQueriable.count(crossinline func: (Long) -> Unit): Disposable = count.subscribe { count -> func(count) } \ No newline at end of file diff --git a/dbflow-rx2/build.gradle b/dbflow-rx2/build.gradle deleted file mode 100644 index 1fb162c01..000000000 --- a/dbflow-rx2/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -apply plugin: 'com.android.library' - -project.ext.artifactId = bt_name - -android { - compileSdkVersion Integer.valueOf(dbflow_target_sdk) - buildToolsVersion dbflow_build_tools_version - - defaultConfig { - minSdkVersion dbflow_min_sdk_rx - targetSdkVersion Integer.valueOf(dbflow_target_sdk) - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } -} - -dependencies { - api project("${dbflow_project_prefix}dbflow") - api 'io.reactivex.rxjava2:rxjava:2.1.3' -} - -apply from: '../android-artifacts.gradle' diff --git a/dbflow-rx2/src/main/AndroidManifest.xml b/dbflow-rx2/src/main/AndroidManifest.xml deleted file mode 100644 index 34ee46575..000000000 --- a/dbflow-rx2/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/CursorResultFlowable.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/CursorResultFlowable.java deleted file mode 100644 index d6922f376..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/CursorResultFlowable.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.language; - -import android.support.annotation.NonNull; -import android.support.annotation.VisibleForTesting; - -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.list.FlowCursorIterator; -import com.raizlabs.android.dbflow.sql.language.CursorResult; - -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.concurrent.atomic.AtomicLong; - -import io.reactivex.Flowable; -import io.reactivex.SingleObserver; -import io.reactivex.disposables.Disposable; - -/** - * Description: Wraps a {@link RXModelQueriable} into a {@link Flowable} - * for each element represented by the query. - */ -public class CursorResultFlowable extends Flowable { - - - @NonNull - private final RXModelQueriable modelQueriable; - - public CursorResultFlowable(@NonNull RXModelQueriable modelQueriable) { - this.modelQueriable = modelQueriable; - - } - - @Override - protected void subscribeActual(final Subscriber subscriber) { - subscriber.onSubscribe(new Subscription() { - @Override - public void request(final long n) { - modelQueriable.queryResults().subscribe(new CursorResultObserver<>(subscriber, n)); - } - - @Override - public void cancel() { - - } - }); - } - - @VisibleForTesting - static class CursorResultObserver implements SingleObserver> { - - private final Subscriber subscriber; - private final long count; - private final AtomicLong emitted; - private final AtomicLong requested; - private Disposable disposable; - - CursorResultObserver(Subscriber subscriber, long count) { - this.subscriber = subscriber; - this.count = count; - requested = new AtomicLong(); - emitted = new AtomicLong(); - } - - @Override - public void onSubscribe(Disposable disposable) { - this.disposable = disposable; - } - - @Override - public void onSuccess(CursorResult ts) { - int starting = this.count == Long.MAX_VALUE && requested.compareAndSet(0, Long.MAX_VALUE) - ? 0 : emitted.intValue(); - long limit = this.count + starting; - - while (limit > 0) { - FlowCursorIterator iterator = ts.iterator(starting, limit); - try { - long i = 0; - while (!disposable.isDisposed() && iterator.hasNext() && i++ < limit) { - subscriber.onNext(iterator.next()); - } - emitted.addAndGet(i); - // no more items - if (!disposable.isDisposed() && i < limit) { - subscriber.onComplete(); - break; - } - limit = requested.addAndGet(-limit); - } catch (Exception e) { - FlowLog.logError(e); - subscriber.onError(e); - } finally { - try { - iterator.close(); - } catch (Exception e) { - FlowLog.logError(e); - subscriber.onError(e); - } - } - } - } - - @Override - public void onError(Throwable e) { - subscriber.onError(e); - } - - } -} diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXModelQueriable.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXModelQueriable.java deleted file mode 100644 index 278c2b16c..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXModelQueriable.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.list.FlowCursorList; -import com.raizlabs.android.dbflow.list.FlowQueryList; -import com.raizlabs.android.dbflow.sql.language.CursorResult; -import com.raizlabs.android.dbflow.sql.language.Join; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.BaseQueryModel; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.List; - -import io.reactivex.Flowable; -import io.reactivex.Maybe; -import io.reactivex.Observable; -import io.reactivex.Single; - -/** - * Description: Mirrors {@link ModelQueriable} with RX constructs. - */ -public interface RXModelQueriable extends RXQueriable { - - @NonNull - Single> queryResults(); - - @NonNull - Single> queryList(); - - @NonNull - Single> queryList(DatabaseWrapper wrapper); - - /** - * @return Single model, the first of potentially many results - */ - @NonNull - Maybe querySingle(); - - /** - * Allows you to specify a DB, useful for migrations. - * - * @return Single model, the first of potentially many results - */ - @NonNull - Maybe querySingle(DatabaseWrapper wrapper); - - /** - * @return Queries for {@link #queryResults()}, and returns one at a time from this {@link Observable} - */ - @NonNull - Flowable queryStreamResults(); - - /** - * @return the table that this query comes from. - */ - @NonNull - Class getTable(); - - /** - * @return A cursor-backed list that handles conversion, retrieval, and caching of lists. Can - * cache models dynamically by setting {@link FlowCursorList#setCacheModels(boolean)} to true. - */ - @NonNull - Single> cursorList(); - - /** - * @return A cursor-backed {@link List} that handles conversion, retrieval, caching, content changes, - * and more. - */ - @NonNull - Single> flowQueryList(); - - /** - * Returns a {@link List} based on the custom {@link TQueryModel} you pass in. - * - * @param queryModelClass The query model class to use. - * @param The class that extends {@link BaseQueryModel} - * @return A list of custom models that are not tied to a table. - */ - @NonNull - Single> queryCustomList(Class queryModelClass); - - /** - * Returns a single {@link TQueryModel} from this query. - * - * @param queryModelClass The class to use. - * @param The class that extends {@link BaseQueryModel} - * @return A single model from the query. - */ - @NonNull - Maybe queryCustomSingle(Class queryModelClass); - - /** - * Disables caching on this query for the object retrieved from DB (if caching enabled). If - * caching is not enabled, this method is ignored. This also disables caching in a {@link FlowCursorList} - * or {@link FlowQueryList} if you {@link #flowQueryList()} or {@link #cursorList()} - */ - @NonNull - RXModelQueriable disableCaching(); - - /** - * @return A new {@link Observable} that observes when the {@link TModel} table changes. - * This can also be multiple tables, given if it results from a {@link Join} (one for each join table). - */ - @NonNull - Flowable> observeOnTableChanges(); -} \ No newline at end of file diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXModelQueriableImpl.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXModelQueriableImpl.java deleted file mode 100644 index a24105a2d..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXModelQueriableImpl.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.list.FlowCursorList; -import com.raizlabs.android.dbflow.list.FlowQueryList; -import com.raizlabs.android.dbflow.sql.language.BaseModelQueriable; -import com.raizlabs.android.dbflow.sql.language.CursorResult; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.List; -import java.util.concurrent.Callable; - -import io.reactivex.BackpressureStrategy; -import io.reactivex.Flowable; -import io.reactivex.Maybe; -import io.reactivex.Single; - -import static io.reactivex.Single.fromCallable; - -/** - * Description: Represents {@link BaseModelQueriable} in RX form. - */ -public class RXModelQueriableImpl extends RXQueriableImpl implements RXModelQueriable { - - private final ModelQueriable modelQueriable; - - public RXModelQueriableImpl(ModelQueriable modelQueriable) { - super(modelQueriable.getTable(), modelQueriable); - this.modelQueriable = modelQueriable; - } - - private ModelQueriable getInnerModelQueriable() { - return modelQueriable; - } - - @NonNull - @Override - public Flowable queryStreamResults() { - return new CursorResultFlowable<>(this); - } - - @NonNull - @Override - public Single> queryResults() { - return fromCallable(new Callable>() { - @Override - public CursorResult call() throws Exception { - return getInnerModelQueriable().queryResults(); - } - }); - } - - @NonNull - @Override - public Single> queryList() { - return fromCallable(new Callable>() { - @Override - public List call() throws Exception { - return getInnerModelQueriable().queryList(); - } - }); - } - - @NonNull - @Override - public Single> queryList(final DatabaseWrapper wrapper) { - return fromCallable(new Callable>() { - @Override - public List call() throws Exception { - return getInnerModelQueriable().queryList(wrapper); - } - }); - } - - @NonNull - @Override - public Maybe querySingle() { - return Maybe.fromCallable(new Callable() { - @Override - public T call() throws Exception { - return getInnerModelQueriable().querySingle(); - } - }); - } - - @NonNull - @Override - public Maybe querySingle(final DatabaseWrapper wrapper) { - return Maybe.fromCallable(new Callable() { - @Override - public T call() throws Exception { - return getInnerModelQueriable().querySingle(wrapper); - } - }); - } - - @NonNull - @Override - public Class getTable() { - return getInnerModelQueriable().getTable(); - } - - @NonNull - @Override - public Single> cursorList() { - return fromCallable(new Callable>() { - @Override - public FlowCursorList call() throws Exception { - return getInnerModelQueriable().cursorList(); - } - }); - } - - @NonNull - @Override - public Single> flowQueryList() { - return fromCallable(new Callable>() { - @Override - public FlowQueryList call() throws Exception { - return getInnerModelQueriable().flowQueryList(); - } - }); - } - - @NonNull - @Override - public Single> queryCustomList( - final Class tQueryModelClass) { - return fromCallable(new Callable>() { - @Override - public List call() throws Exception { - return getInnerModelQueriable().queryCustomList(tQueryModelClass); - } - }); - } - - @NonNull - @Override - public Maybe queryCustomSingle( - final Class tQueryModelClass) { - return Maybe.fromCallable(new Callable() { - @Override - public TQueryModel call() throws Exception { - return getInnerModelQueriable().queryCustomSingle(tQueryModelClass); - } - }); - } - - @NonNull - @Override - public RXModelQueriable disableCaching() { - getInnerModelQueriable().disableCaching(); - return this; - } - - @NonNull - @Override - public Flowable> observeOnTableChanges() { - return Flowable.create(new TableChangeOnSubscribe<>(getInnerModelQueriable()), - BackpressureStrategy.LATEST); - } -} diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXQueriable.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXQueriable.java deleted file mode 100644 index e2c121101..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXQueriable.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.language; - -import android.database.Cursor; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.language.Delete; -import com.raizlabs.android.dbflow.sql.language.Insert; -import com.raizlabs.android.dbflow.sql.language.Set; -import com.raizlabs.android.dbflow.sql.queriable.Queriable; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import io.reactivex.Completable; -import io.reactivex.Maybe; -import io.reactivex.Single; - -/** - * Description: Mirrors {@link Queriable} with RX constructs. - */ -public interface RXQueriable { - - /** - * @return An {@link Single} from the DB based on this query - */ - @NonNull - Maybe query(); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @param databaseWrapper The wrapper to pass in. - * @return An {@link Single} from the DB based on this query - */ - @NonNull - Maybe query(DatabaseWrapper databaseWrapper); - - - /** - * @return An {@link Single} of {@link DatabaseStatement} from this query. - */ - @NonNull - Single compileStatement(); - - /** - * @param databaseWrapper The wrapper to use. - * @return An {@link Single} of {@link DatabaseStatement} from this query with database specified. - */ - @NonNull - Single compileStatement(DatabaseWrapper databaseWrapper); - - /** - * @return the count of the results of the query. - * @deprecated use {@link #longValue()} - */ - @NonNull - Single count(); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @return the count of the results of the query. - * @deprecated use {@link #longValue(DatabaseWrapper)} - */ - @NonNull - Single count(DatabaseWrapper databaseWrapper); - - /** - * @return the long value of this query. - **/ - @NonNull - Single longValue(); - - /** - * @return the long value of this query. - **/ - @NonNull - Single longValue(DatabaseWrapper databaseWrapper); - - /** - * @return This may return the number of rows affected from a {@link Insert} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - @NonNull - Single executeInsert(); - - /** - * @return This may return the number of rows affected from a {@link Insert} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - @NonNull - Single executeInsert(DatabaseWrapper databaseWrapper); - - /** - * @return This may return the number of rows affected from a {@link Set} or {@link Delete} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - @NonNull - Single executeUpdateDelete(DatabaseWrapper databaseWrapper); - - /** - * @return This may return the number of rows affected from a {@link Set} or {@link Delete} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - @NonNull - Single executeUpdateDelete(); - - /** - * @return True if this query has data. It will run a {@link #count()} greater than 0. - */ - @NonNull - Single hasData(); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @return True if this query has data. It will run a {@link #count()} greater than 0. - */ - @NonNull - Single hasData(DatabaseWrapper databaseWrapper); - - /** - * Will not return a result, rather simply will execute a SQL statement. Use this for non-SELECT statements or when - * you're not interested in the result. - */ - @NonNull - Completable execute(); - - /** - * Will not return a result, rather simply will execute a SQL statement. Use this for non-SELECT statements or when - * you're not interested in the result. - */ - @NonNull - Completable execute(DatabaseWrapper databaseWrapper); -} diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXQueriableImpl.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXQueriableImpl.java deleted file mode 100644 index a33c8acf9..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXQueriableImpl.java +++ /dev/null @@ -1,213 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.language; - -import android.database.Cursor; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.language.BaseQueriable; -import com.raizlabs.android.dbflow.sql.queriable.Queriable; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.concurrent.Callable; - -import io.reactivex.Completable; -import io.reactivex.Maybe; -import io.reactivex.Single; - -import static io.reactivex.Single.fromCallable; - -/** - * Description: Represents {@link BaseQueriable} with RX constructs. - */ -public class RXQueriableImpl implements RXQueriable { - - private final Class table; - private final Queriable queriable; - - public RXQueriableImpl(Class table, Queriable queriable) { - this.table = table; - this.queriable = queriable; - } - - @NonNull - private Queriable getInnerQueriable() { - return queriable; - } - - @NonNull - @Override - public Maybe query() { - return Maybe.fromCallable(new Callable() { - @Override - public Cursor call() throws Exception { - return getInnerQueriable().query(); - } - }); - } - - @NonNull - @Override - public Maybe query(final DatabaseWrapper databaseWrapper) { - return Maybe.fromCallable(new Callable() { - @Override - public Cursor call() throws Exception { - return getInnerQueriable().query(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single compileStatement() { - return fromCallable(new Callable() { - @Override - public DatabaseStatement call() throws Exception { - return getInnerQueriable().compileStatement(); - } - }); - } - - @NonNull - @Override - public Single compileStatement(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public DatabaseStatement call() throws Exception { - return getInnerQueriable().compileStatement(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single count() { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().count(); - } - }); - } - - @NonNull - @Override - public Single count(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().count(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single longValue() { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().longValue(); - } - }); - } - - @NonNull - @Override - public Single longValue(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().longValue(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single executeInsert() { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().executeInsert(FlowManager.getWritableDatabaseForTable(table)); - } - }); - } - - @NonNull - @Override - public Single executeInsert(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().executeInsert(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single executeUpdateDelete(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().executeUpdateDelete(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Single executeUpdateDelete() { - return fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return getInnerQueriable().executeUpdateDelete(); - } - }); - } - - @NonNull - @Override - public Single hasData() { - return fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return getInnerQueriable().hasData(); - } - }); - } - - @NonNull - @Override - public Single hasData(final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return getInnerQueriable().hasData(databaseWrapper); - } - }); - } - - @NonNull - @Override - public Completable execute() { - return Completable.fromRunnable(new Runnable() { - @Override - public void run() { - getInnerQueriable().execute(); - } - }); - } - - @NonNull - @Override - public Completable execute(final DatabaseWrapper databaseWrapper) { - return Completable.fromRunnable(new Runnable() { - @Override - public void run() { - getInnerQueriable().execute(databaseWrapper); - } - }); - } -} diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXSQLite.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXSQLite.java deleted file mode 100644 index ad710392b..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/RXSQLite.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.sql.queriable.Queriable; - -/** - * Description: The RX implementation of SQLite language queries. Meant to be interchangeable - * with {@link SQLite}. - */ -public class RXSQLite { - - @NonNull - public static RXModelQueriableImpl rx(ModelQueriable modelQueriable) { - return new RXModelQueriableImpl<>(modelQueriable); - } - - @NonNull - public static RXQueriableImpl rx(Class table, Queriable queriable) { - return new RXQueriableImpl(table, queriable); - } - -} diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/TableChangeOnSubscribe.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/TableChangeOnSubscribe.java deleted file mode 100644 index e98a0288d..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/language/TableChangeOnSubscribe.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.runtime.OnTableChangedListener; -import com.raizlabs.android.dbflow.runtime.TableNotifierRegister; -import com.raizlabs.android.dbflow.sql.language.From; -import com.raizlabs.android.dbflow.sql.language.Join; -import com.raizlabs.android.dbflow.sql.language.Where; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.BaseModel; - -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; -import io.reactivex.disposables.Disposables; - -/** - * Description: Emits when table changes occur for the related table on the {@link ModelQueriable}. - * If the {@link ModelQueriable} relates to a {@link Join}, this can be multiple tables. - */ -public class TableChangeOnSubscribe implements FlowableOnSubscribe> { - - private final ModelQueriable modelQueriable; - - private final TableNotifierRegister register; - private FlowableEmitter> flowableEmitter; - - public TableChangeOnSubscribe(ModelQueriable modelQueriable) { - this.modelQueriable = modelQueriable; - register = FlowManager.newRegisterForTable(modelQueriable.getTable()); - } - - @Override - public void subscribe(FlowableEmitter> e) throws Exception { - flowableEmitter = e; - flowableEmitter.setDisposable(Disposables.fromRunnable(new Runnable() { - @Override - public void run() { - register.unregisterAll(); - } - })); - - From from = null; - if (modelQueriable instanceof From) { - from = (From) modelQueriable; - } else if (modelQueriable instanceof Where - && ((Where) modelQueriable).getWhereBase() instanceof From) { - //noinspection unchecked - from = (From) ((Where) modelQueriable).getWhereBase(); - } - - // From could be part of many joins, so we register for all affected tables here. - if (from != null) { - java.util.Set> associatedTables = from.getAssociatedTables(); - for (Class table : associatedTables) { - register.register(table); - } - } else { - register.register(modelQueriable.getTable()); - } - - register.setListener(onTableChangedListener); - flowableEmitter.onNext(modelQueriable); - } - - private final OnTableChangedListener onTableChangedListener - = new OnTableChangedListener() { - @Override - public void onTableChanged(@Nullable Class tableChanged, @NonNull BaseModel.Action action) { - if (modelQueriable.getTable().equals(tableChanged)) { - flowableEmitter.onNext(modelQueriable); - } - } - }; - -} diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/BaseRXModel.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/BaseRXModel.java deleted file mode 100644 index 274a2fea7..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/BaseRXModel.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.structure; - -import com.raizlabs.android.dbflow.annotation.ColumnIgnore; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.InvalidDBConfiguration; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import io.reactivex.Completable; -import io.reactivex.Single; - -/** - * Description: Similar to {@link BaseModel} with RX constructs. Extend this for convenience methods. - */ -@SuppressWarnings("unchecked") -public class BaseRXModel { - - @ColumnIgnore - private transient RXModelAdapter modelAdapter; - - public Single save() { - return getRXModelAdapter().save(this); - } - - public Single save(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().save(this, databaseWrapper); - } - - public Completable load() { - return getRXModelAdapter().load(this); - } - - public Completable load(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().load(this, databaseWrapper); - } - - public Single delete() { - return getRXModelAdapter().delete(this); - } - - public Single delete(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().delete(this, databaseWrapper); - } - - public Single update() { - return getRXModelAdapter().update(this); - } - - public Single update(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().update(this, databaseWrapper); - } - - public Single insert() { - return getRXModelAdapter().insert(this); - } - - public Single insert(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().insert(this, databaseWrapper); - } - - public Single exists() { - return getRXModelAdapter().exists(this); - } - - - public Single exists(DatabaseWrapper databaseWrapper) { - return getRXModelAdapter().exists(this, databaseWrapper); - } - - /** - * @return The associated {@link ModelAdapter}. The {@link FlowManager} - * may throw a {@link InvalidDBConfiguration} for this call if this class - * is not associated with a table, so be careful when using this method. - */ - private RXModelAdapter getRXModelAdapter() { - if (modelAdapter == null) { - modelAdapter = new RXModelAdapter<>(getClass()); - } - return modelAdapter; - } -} diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/RXModelAdapter.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/RXModelAdapter.java deleted file mode 100644 index 406934ee2..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/RXModelAdapter.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.structure; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.Collection; -import java.util.concurrent.Callable; - -import io.reactivex.Completable; -import io.reactivex.Single; - -import static io.reactivex.Completable.fromCallable; - -/** - * Description: Wraps most {@link ModelAdapter} modification operations into RX-style constructs. - */ -public class RXModelAdapter extends RXRetrievalAdapter { - - public static RXModelAdapter from(ModelAdapter modelAdapter) { - return new RXModelAdapter<>(modelAdapter); - } - - public static RXModelAdapter from(Class table) { - return new RXModelAdapter<>(table); - } - - private final ModelAdapter modelAdapter; - - RXModelAdapter(ModelAdapter modelAdapter) { - super(modelAdapter); - this.modelAdapter = modelAdapter; - } - - public RXModelAdapter(Class table) { - this(FlowManager.getModelAdapter(table)); - } - - public Single save(final T model) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.save(model); - } - }); - } - - public Single save(final T model, final DatabaseWrapper databaseWrapper) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.save(model, databaseWrapper); - } - }); - } - - public Completable saveAll(final Collection models) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.saveAll(models); - return null; - } - }); - } - - public Completable saveAll(final Collection models, final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.saveAll(models, databaseWrapper); - return null; - } - }); - } - - public Single insert(final T model) { - return Single.fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return modelAdapter.insert(model); - } - }); - } - - public Single insert(final T model, final DatabaseWrapper databaseWrapper) { - return Single.fromCallable(new Callable() { - @Override - public Long call() throws Exception { - return modelAdapter.insert(model, databaseWrapper); - } - }); - } - - public Completable insertAll(final Collection models) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.insertAll(models); - return null; - } - }); - } - - public Completable insertAll(final Collection models, - final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.insertAll(models, databaseWrapper); - return null; - } - }); - } - - public Single update(final T model) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.update(model); - } - }); - } - - public Single update(final T model, final DatabaseWrapper databaseWrapper) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.update(model, databaseWrapper); - } - }); - } - - public Completable updateAll(final Collection models) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.updateAll(models); - return null; - } - }); - } - - public Completable updateAll(final Collection models, final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.updateAll(models, databaseWrapper); - return null; - } - }); - } - - public Single delete(final T model) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.delete(model); - } - }); - } - - public Single delete(final T model, final DatabaseWrapper databaseWrapper) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return modelAdapter.delete(model, databaseWrapper); - } - }); - } - - public Completable deleteAll(final Collection models) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.deleteAll(models); - return null; - } - }); - } - - public Completable deleteAll(final Collection models, final DatabaseWrapper databaseWrapper) { - return fromCallable(new Callable() { - @Override - public Void call() throws Exception { - modelAdapter.deleteAll(models, databaseWrapper); - return null; - } - }); - } -} diff --git a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/RXRetrievalAdapter.java b/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/RXRetrievalAdapter.java deleted file mode 100644 index 16a46d891..000000000 --- a/dbflow-rx2/src/main/java/com/raizlabs/android/dbflow/rx2/structure/RXRetrievalAdapter.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.raizlabs.android.dbflow.rx2.structure; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.RetrievalAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.concurrent.Callable; - -import io.reactivex.Completable; -import io.reactivex.Single; - -/** - * Description: Mirrors the {@link RetrievalAdapter} with subset of exposed methods, mostly for - * {@link #load(Object)} and {@link #exists(Object)} - */ -public class RXRetrievalAdapter { - - public static RXRetrievalAdapter from(RetrievalAdapter modelAdapter) { - return new RXRetrievalAdapter<>(modelAdapter); - } - - public static RXRetrievalAdapter from(Class table) { - return new RXRetrievalAdapter<>(table); - } - - private final RetrievalAdapter retrievalAdapter; - - RXRetrievalAdapter(RetrievalAdapter retrievalAdapter) { - this.retrievalAdapter = retrievalAdapter; - } - - RXRetrievalAdapter(Class table) { - this(FlowManager.getInstanceAdapter(table)); - } - - public Completable load(final TModel model) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - retrievalAdapter.load(model); - return null; - } - }); - } - - public Completable load(final TModel model, final DatabaseWrapper databaseWrapper) { - return Completable.fromCallable(new Callable() { - @Override - public Void call() throws Exception { - retrievalAdapter.load(model, databaseWrapper); - return null; - } - }); - } - - /** - * @param model The model to query values from - * @return True if it exists as a row in the corresponding database table - */ - public Single exists(final TModel model) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return retrievalAdapter.exists(model); - } - }); - } - - public Single exists(final TModel model, final DatabaseWrapper wrapper) { - return Single.fromCallable(new Callable() { - @Override - public Boolean call() throws Exception { - return retrievalAdapter.exists(model, wrapper); - } - }); - } -} diff --git a/dbflow-sqlcipher/build.gradle b/dbflow-sqlcipher/build.gradle deleted file mode 100644 index 74428766d..000000000 --- a/dbflow-sqlcipher/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply plugin: 'com.android.library' - -project.ext.artifactId = bt_name - -android { - compileSdkVersion 26 - buildToolsVersion dbflow_build_tools_version - - defaultConfig { - minSdkVersion 7 - targetSdkVersion 26 - versionCode = version_code - } - - lintOptions { - abortOnError false - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } -} - -dependencies { - api "net.zetetic:android-database-sqlcipher:3.5.7@aar" - api project("${dbflow_project_prefix}dbflow") -} - -apply from: '../android-artifacts.gradle' diff --git a/dbflow-sqlcipher/src/main/AndroidManifest.xml b/dbflow-sqlcipher/src/main/AndroidManifest.xml deleted file mode 100644 index fa75ae33c..000000000 --- a/dbflow-sqlcipher/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherDatabase.java b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherDatabase.java deleted file mode 100644 index eb78f63ef..000000000 --- a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherDatabase.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.raizlabs.android.dbflow.sqlcipher; - -import android.content.ContentValues; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import net.sqlcipher.database.SQLiteDatabase; - -/** - * Description: Implements the code necessary to use a {@link SQLiteDatabase} in dbflow. - */ -public class SQLCipherDatabase implements DatabaseWrapper { - - private final SQLiteDatabase database; - - public static SQLCipherDatabase from(SQLiteDatabase database) { - return new SQLCipherDatabase(database); - } - - SQLCipherDatabase(SQLiteDatabase database) { - this.database = database; - } - - @Override - public void execSQL(@NonNull String query) { - database.execSQL(query); - } - - @Override - public void beginTransaction() { - database.beginTransaction(); - } - - @Override - public void setTransactionSuccessful() { - database.setTransactionSuccessful(); - } - - @Override - public void endTransaction() { - database.endTransaction(); - } - - @Override - public int getVersion() { - return database.getVersion(); - } - - @NonNull - @Override - public DatabaseStatement compileStatement(@NonNull String rawQuery) { - return SQLCipherStatement.from(database.compileStatement(rawQuery)); - } - - public SQLiteDatabase getDatabase() { - return database; - } - - @NonNull - @Override - public FlowCursor rawQuery(@NonNull String query, @Nullable String[] selectionArgs) { - return FlowCursor.from(database.rawQuery(query, selectionArgs)); - } - - @Override - public long updateWithOnConflict(@NonNull String tableName, @NonNull ContentValues contentValues, @Nullable String where, @Nullable String[] whereArgs, int conflictAlgorithm) { - return database.updateWithOnConflict(tableName, contentValues, where, whereArgs, conflictAlgorithm); - } - - @Override - public long insertWithOnConflict(@NonNull String tableName, @Nullable String nullColumnHack, @NonNull ContentValues values, int sqLiteDatabaseAlgorithmInt) { - return database.insertWithOnConflict(tableName, nullColumnHack, values, sqLiteDatabaseAlgorithmInt); - } - - @NonNull - @Override - public FlowCursor query(@NonNull String tableName, - @Nullable String[] columns, - @Nullable String selection, - @Nullable String[] selectionArgs, - @Nullable String groupBy, - @Nullable String having, - @Nullable String orderBy) { - return FlowCursor.from(database.query(tableName, columns, selection, selectionArgs, groupBy, having, orderBy)); - } - - @Override - public int delete(@NonNull String tableName, @Nullable String whereClause, @Nullable String[] whereArgs) { - return database.delete(tableName, whereClause, whereArgs); - } -} diff --git a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherOpenHelper.java b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherOpenHelper.java deleted file mode 100644 index ea34301a5..000000000 --- a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherOpenHelper.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.raizlabs.android.dbflow.sqlcipher; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseConfig; -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.database.BaseDatabaseHelper; -import com.raizlabs.android.dbflow.structure.database.DatabaseHelperDelegate; -import com.raizlabs.android.dbflow.structure.database.DatabaseHelperListener; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.OpenHelper; - -import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteOpenHelper; - -/** - * Description: The replacement {@link OpenHelper} for SQLCipher. Specify a subclass of this is {@link DatabaseConfig#databaseClass()} - * of your database to get it to work with specifying the secret you use for the database. - */ -public abstract class SQLCipherOpenHelper extends SQLiteOpenHelper implements OpenHelper { - - private DatabaseHelperDelegate databaseHelperDelegate; - private SQLCipherDatabase cipherDatabase; - - public SQLCipherOpenHelper(DatabaseDefinition databaseDefinition, DatabaseHelperListener listener) { - super(FlowManager.getContext(), databaseDefinition.isInMemory() ? null : databaseDefinition.getDatabaseFileName(), null, databaseDefinition.getDatabaseVersion()); - SQLiteDatabase.loadLibs(FlowManager.getContext()); - - OpenHelper backupHelper = null; - if (databaseDefinition.backupEnabled()) { - // Temp database mirrors existing - backupHelper = new BackupHelper(FlowManager.getContext(), DatabaseHelperDelegate.getTempDbFileName(databaseDefinition), - databaseDefinition.getDatabaseVersion(), databaseDefinition); - } - - databaseHelperDelegate = new DatabaseHelperDelegate(listener, databaseDefinition, backupHelper); - } - - @Override - public void performRestoreFromBackup() { - databaseHelperDelegate.performRestoreFromBackup(); - } - - @Nullable - @Override - public DatabaseHelperDelegate getDelegate() { - return databaseHelperDelegate; - } - - @Override - public boolean isDatabaseIntegrityOk() { - return databaseHelperDelegate.isDatabaseIntegrityOk(); - } - - @Override - public void backupDB() { - databaseHelperDelegate.backupDB(); - } - - @NonNull - @Override - public DatabaseWrapper getDatabase() { - if (cipherDatabase == null || !cipherDatabase.getDatabase().isOpen()) { - cipherDatabase = SQLCipherDatabase.from(getWritableDatabase(getCipherSecret())); - } - return cipherDatabase; - } - - /** - * Set a listener to listen for specific DB events and perform an action before we execute this classes - * specific methods. - * - * @param listener - */ - public void setDatabaseListener(@Nullable DatabaseHelperListener listener) { - databaseHelperDelegate.setDatabaseHelperListener(listener); - } - - @Override - public void onCreate(SQLiteDatabase db) { - databaseHelperDelegate.onCreate(SQLCipherDatabase.from(db)); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - databaseHelperDelegate.onUpgrade(SQLCipherDatabase.from(db), oldVersion, newVersion); - } - - @Override - public void onOpen(SQLiteDatabase db) { - databaseHelperDelegate.onOpen(SQLCipherDatabase.from(db)); - } - - @Override - public void closeDB() { - getDatabase(); - cipherDatabase.getDatabase().close(); - } - - /** - * @return The SQLCipher secret for opening this database. - */ - protected abstract String getCipherSecret(); - - /** - * Simple helper to manage backup. - */ - private class BackupHelper extends SQLiteOpenHelper implements OpenHelper { - - private SQLCipherDatabase sqlCipherDatabase; - private final BaseDatabaseHelper baseDatabaseHelper; - - public BackupHelper(Context context, String name, int version, DatabaseDefinition databaseDefinition) { - super(context, name, null, version); - this.baseDatabaseHelper = new BaseDatabaseHelper(databaseDefinition); - } - - @NonNull - @Override - public DatabaseWrapper getDatabase() { - if (sqlCipherDatabase == null) { - sqlCipherDatabase = SQLCipherDatabase.from(getWritableDatabase(getCipherSecret())); - } - return sqlCipherDatabase; - } - - @Override - public void performRestoreFromBackup() { - } - - @Nullable - @Override - public DatabaseHelperDelegate getDelegate() { - return null; - } - - @Override - public boolean isDatabaseIntegrityOk() { - return false; - } - - @Override - public void backupDB() { - } - - @Override - public void closeDB() { - } - - @Override - public void setDatabaseListener(@Nullable DatabaseHelperListener helperListener) { - } - - @Override - public void onCreate(SQLiteDatabase db) { - baseDatabaseHelper.onCreate(SQLCipherDatabase.from(db)); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - baseDatabaseHelper.onUpgrade(SQLCipherDatabase.from(db), oldVersion, newVersion); - } - - @Override - public void onOpen(SQLiteDatabase db) { - baseDatabaseHelper.onOpen(SQLCipherDatabase.from(db)); - } - } -} diff --git a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java b/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java deleted file mode 100644 index 6c51130d7..000000000 --- a/dbflow-sqlcipher/src/main/java/com/raizlabs/android/dbflow/sqlcipher/SQLCipherStatement.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.raizlabs.android.dbflow.sqlcipher; - -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.structure.database.BaseDatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; - -import net.sqlcipher.database.SQLiteStatement; - -/** - * Description: Implements the methods necessary for {@link DatabaseStatement}. Delegates calls to - * the contained {@link SQLiteStatement}. - */ -public class SQLCipherStatement extends BaseDatabaseStatement { - - public static SQLCipherStatement from(SQLiteStatement statement) { - return new SQLCipherStatement(statement); - } - - private final SQLiteStatement statement; - - SQLCipherStatement(SQLiteStatement statement) { - this.statement = statement; - } - - public SQLiteStatement getStatement() { - return statement; - } - - @Override - public long executeUpdateDelete() { - return statement.executeUpdateDelete(); - } - - @Override - public void execute() { - statement.execute(); - } - - @Override - public void close() { - statement.close(); - } - - @Override - public long simpleQueryForLong() { - return statement.simpleQueryForLong(); - } - - @Nullable - @Override - public String simpleQueryForString() { - return statement.simpleQueryForString(); - } - - @Override - public long executeInsert() { - return statement.executeInsert(); - } - - @Override - public void bindString(int index, String s) { - statement.bindString(index, s); - } - - @Override - public void bindNull(int index) { - statement.bindNull(index); - } - - @Override - public void bindLong(int index, long aLong) { - statement.bindLong(index, aLong); - } - - @Override - public void bindDouble(int index, double aDouble) { - statement.bindDouble(index, aDouble); - } - - @Override - public void bindBlob(int index, byte[] bytes) { - statement.bindBlob(index, bytes); - } -} diff --git a/dbflow-tests/build.gradle b/dbflow-tests/build.gradle deleted file mode 100644 index d1ec2f4d7..000000000 --- a/dbflow-tests/build.gradle +++ /dev/null @@ -1,97 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'com.getkeepsafe.dexcount' -apply plugin: 'kotlin-kapt' - -android { - - useLibrary 'org.apache.http.legacy' - - compileSdkVersion 26 - buildToolsVersion dbflow_build_tools_version - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - - defaultConfig { - minSdkVersion 15 - targetSdkVersion 26 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - - lintOptions { - abortOnError false - } - - packagingOptions { - exclude 'META-INF/services/javax.annotation.processing.Processor' - exclude 'META-INF/rxjava.properties' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - testOptions { - unitTests { - includeAndroidResources true - } - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - - kapt project("${dbflow_project_prefix}dbflow-processor") - implementation project(':dbflow') - implementation 'com.android.support:appcompat-v7:26.0.1' - implementation project("${dbflow_project_prefix}dbflow") - implementation project("${dbflow_project_prefix}dbflow-sqlcipher") - implementation project("${dbflow_project_prefix}dbflow-kotlinextensions") - implementation project("${dbflow_project_prefix}dbflow-rx") - implementation project("${dbflow_project_prefix}dbflow-rx-kotlinextensions") - implementation project("${dbflow_project_prefix}dbflow-rx2") - implementation project("${dbflow_project_prefix}dbflow-rx2-kotlinextensions") - - kaptTest project("${dbflow_project_prefix}dbflow-processor") - kaptAndroidTest project("${dbflow_project_prefix}dbflow-processor") - - testImplementation 'org.glassfish:javax.annotation:10.0-b28' - - testImplementation 'junit:junit:4.12' - testImplementation "org.robolectric:robolectric:3.4.2" - testImplementation("com.nhaarman:mockito-kotlin:1.5.0") { - exclude group: "org.jetbrains.kotlin" - } - testImplementation 'org.mockito:mockito-core:2.8.9' - - androidTestImplementation 'junit:junit:4.12' - androidTestImplementation('com.android.support.test:runner:0.5') { - exclude group: 'com.android.support', module: 'support-annotations' - } - androidTestImplementation('com.android.support.test:rules:0.5') { - exclude group: 'com.android.support', module: 'support-annotations' - } - androidTestImplementation 'org.awaitility:awaitility:3.0.0-rc1' - -} - -android.applicationVariants.all { variant -> - String taskName = "copy${variant.name.capitalize()}ResDirectoryToClasses" - task(taskName, type: Copy) { - from "${projectDir}/src/test/res" - into "${buildDir}/intermediates/classes/test/${variant.buildType.name}/res" - - from "${projectDir}/src/test/assets" - into "${buildDir}/intermediates/classes/test/${variant.buildType.name}/assets" - } - project.getTasksByName("generate${variant.name.capitalize()}Resources", false)[0].dependsOn(taskName) -} - -dexcount { - includeClasses = true - orderByMethodCount = true -} \ No newline at end of file diff --git a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/DBFlowInstrumentedTestRule.kt b/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/DBFlowInstrumentedTestRule.kt deleted file mode 100644 index 3ddedd217..000000000 --- a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/DBFlowInstrumentedTestRule.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.raizlabs.android.dbflow - -import com.raizlabs.android.dbflow.config.DatabaseConfig -import com.raizlabs.android.dbflow.config.FlowConfig -import com.raizlabs.android.dbflow.config.FlowManager -import com.raizlabs.android.dbflow.prepackaged.PrepackagedDB -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement - -class DBFlowInstrumentedTestRule : TestRule { - - override fun apply(base: Statement, description: Description): Statement { - return object : Statement() { - - @Throws(Throwable::class) - override fun evaluate() { - FlowManager.init(FlowConfig.Builder(DemoApp.context) - .addDatabaseConfig(DatabaseConfig.Builder(AppDatabase::class.java) - .transactionManagerCreator(::ImmediateTransactionManager) - .build()) - .addDatabaseConfig(DatabaseConfig.builder(PrepackagedDB::class.java) - .databaseName("prepackaged") - .build()) - .build()) - try { - base.evaluate() - } finally { - FlowManager.destroy() - } - } - } - } - - companion object { - fun create() = DBFlowInstrumentedTestRule() - } -} \ No newline at end of file diff --git a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/ImmediateTransactionManager.kt b/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/ImmediateTransactionManager.kt deleted file mode 100644 index 6dcd865c6..000000000 --- a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/ImmediateTransactionManager.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.raizlabs.android.dbflow - -import com.raizlabs.android.dbflow.config.DatabaseDefinition -import com.raizlabs.android.dbflow.runtime.BaseTransactionManager -import com.raizlabs.android.dbflow.structure.database.transaction.ITransactionQueue -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction - -/** - * Description: Executes all transactions on same thread for testing. - */ -class ImmediateTransactionManager(databaseDefinition: DatabaseDefinition) - : BaseTransactionManager(ImmediateTransactionQueue(), databaseDefinition) - - -class ImmediateTransactionQueue : ITransactionQueue { - - override fun add(transaction: Transaction) { - transaction.newBuilder() - .runCallbacksOnSameThread(true) - .build() - .executeSync() - } - - override fun cancel(transaction: Transaction) { - - } - - override fun startIfNotAlive() { - } - - override fun cancel(name: String) { - } - - override fun quit() { - } - -} \ No newline at end of file diff --git a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/prepackaged/PrepackagedDBTest.kt b/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/prepackaged/PrepackagedDBTest.kt deleted file mode 100644 index d31d543e3..000000000 --- a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/prepackaged/PrepackagedDBTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.raizlabs.android.dbflow.prepackaged - -import com.raizlabs.android.dbflow.BaseInstrumentedUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.list -import com.raizlabs.android.dbflow.kotlinextensions.select -import org.junit.Assert.assertTrue -import org.junit.Test - -/** - * Description: Asserts our prepackaged DB loads. - */ -class PrepackagedDBTest : BaseInstrumentedUnitTest() { - - @Test - fun assertWeCanLoadFromDB() { - val list = (select from Dog::class).list - - assertTrue(!list.isEmpty()) - } -} diff --git a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/sqlcipher/CipherTest.kt b/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/sqlcipher/CipherTest.kt deleted file mode 100644 index 1a7231511..000000000 --- a/dbflow-tests/src/androidTest/java/com/raizlabs/android/dbflow/sqlcipher/CipherTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.raizlabs.android.dbflow.sqlcipher - -import com.raizlabs.android.dbflow.BaseInstrumentedUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.result -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.where -import com.raizlabs.android.dbflow.sql.language.Delete -import org.junit.Assert.assertTrue -import org.junit.Test - -/** - * Description: Ensures we can use SQLCipher - */ -class CipherTest : BaseInstrumentedUnitTest() { - - @Test - fun testCipherModel() { - Delete.table(CipherModel::class.java) - val model = CipherModel(name = "name") - model.save() - - assertTrue(model.exists()) - - val retrieval = (select from CipherModel::class where CipherModel_Table.name.eq("name")).result - assertTrue(retrieval!!.id == model.id) - Delete.table(CipherModel::class.java) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/main/AndroidManifest.xml b/dbflow-tests/src/main/AndroidManifest.xml deleted file mode 100644 index 201a00531..000000000 --- a/dbflow-tests/src/main/AndroidManifest.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/AppDatabase.kt b/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/AppDatabase.kt deleted file mode 100644 index 01ee859f9..000000000 --- a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/AppDatabase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.raizlabs.android.dbflow - -import com.raizlabs.android.dbflow.annotation.Database - -@Database(version = AppDatabase.VERSION) -object AppDatabase { - - const val VERSION = 1 - -} \ No newline at end of file diff --git a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/User.kt b/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/User.kt deleted file mode 100644 index 583d61896..000000000 --- a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/User.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.raizlabs.android.dbflow - -import com.raizlabs.android.dbflow.annotation.Column -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table - -@Table(database = AppDatabase::class, name = "User2") -class User { - - @PrimaryKey - var id: Int = 0 - - @Column - var firstName: String? = null - - @Column - var lastName: String? = null - - @Column - var email: String? = null -} diff --git a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/contentobserver/User.kt b/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/contentobserver/User.kt deleted file mode 100644 index 5d5da0878..000000000 --- a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/contentobserver/User.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.raizlabs.android.dbflow.contentobserver - -import com.raizlabs.android.dbflow.AppDatabase -import com.raizlabs.android.dbflow.annotation.Column -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table - -@Table(database = AppDatabase::class) -class User(@PrimaryKey var id: Int = 0, @PrimaryKey var name: String = "", @Column var age: Int = 0) \ No newline at end of file diff --git a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/prepackaged/PrepackagedDB.kt b/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/prepackaged/PrepackagedDB.kt deleted file mode 100644 index 464fd34c4..000000000 --- a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/prepackaged/PrepackagedDB.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.raizlabs.android.dbflow.prepackaged - -import com.raizlabs.android.dbflow.annotation.Column -import com.raizlabs.android.dbflow.annotation.Database -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table -import com.raizlabs.android.dbflow.structure.BaseModel - -@Database(version = PrepackagedDB.VERSION) -object PrepackagedDB { - const val VERSION = 1 -} - -@Table(database = PrepackagedDB::class, allFields = true) -class Dog : BaseModel() { - - @PrimaryKey - var id: Int = 0 - - @Column - var breed: String? = null - - @Column - var color: String? = null -} \ No newline at end of file diff --git a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/sqlcipher/CipherDatabase.kt b/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/sqlcipher/CipherDatabase.kt deleted file mode 100644 index 5c4911f7f..000000000 --- a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/sqlcipher/CipherDatabase.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.raizlabs.android.dbflow.sqlcipher - -import com.raizlabs.android.dbflow.annotation.Database - -@Database(version = CipherDatabase.VERSION) -object CipherDatabase { - - const val VERSION = 1 -} \ No newline at end of file diff --git a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/sqlcipher/CipherTestObjects.kt b/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/sqlcipher/CipherTestObjects.kt deleted file mode 100644 index d0b6c065a..000000000 --- a/dbflow-tests/src/main/java/com/raizlabs/android/dbflow/sqlcipher/CipherTestObjects.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.raizlabs.android.dbflow.sqlcipher - -import com.raizlabs.android.dbflow.annotation.Column -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table -import com.raizlabs.android.dbflow.config.DatabaseDefinition -import com.raizlabs.android.dbflow.structure.BaseModel -import com.raizlabs.android.dbflow.structure.database.DatabaseHelperListener - -class SQLCipherOpenHelperImpl(databaseDefinition: DatabaseDefinition, helperListener: DatabaseHelperListener?) - : SQLCipherOpenHelper(databaseDefinition, helperListener) { - override fun getCipherSecret() = "dbflow-rules" -} - -@Table(database = CipherDatabase::class) -class CipherModel(@PrimaryKey(autoincrement = true) var id: Long = 0, - @Column var name: String? = null) : BaseModel() \ No newline at end of file diff --git a/dbflow-tests/src/main/res/menu/menu_demo.xml b/dbflow-tests/src/main/res/menu/menu_demo.xml deleted file mode 100644 index 77c35819a..000000000 --- a/dbflow-tests/src/main/res/menu/menu_demo.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/dbflow-tests/src/test/AndroidManifest.xml b/dbflow-tests/src/test/AndroidManifest.xml deleted file mode 100644 index 447bd481b..000000000 --- a/dbflow-tests/src/test/AndroidManifest.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/BaseUnitTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/BaseUnitTest.kt deleted file mode 100644 index 7dad0f0aa..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/BaseUnitTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow - -import android.content.Context -import org.junit.Rule -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config - -@RunWith(RobolectricTestRunner::class) -@Config(manifest = Config.NONE) -abstract class BaseUnitTest { - - @JvmField - @Rule - var dblflowTestRule = DBFlowTestRule.create() - - val context: Context - get() = RuntimeEnvironment.application -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/DBFlowTestRule.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/DBFlowTestRule.kt deleted file mode 100644 index 261fb65c3..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/DBFlowTestRule.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.raizlabs.android.dbflow - -import com.raizlabs.android.dbflow.config.DatabaseConfig -import com.raizlabs.android.dbflow.config.FlowConfig -import com.raizlabs.android.dbflow.config.FlowManager -import com.raizlabs.android.dbflow.contentprovider.ContentDatabase -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import org.robolectric.RuntimeEnvironment - -class DBFlowTestRule : TestRule { - - override fun apply(base: Statement, description: Description): Statement { - return object : Statement() { - - @Throws(Throwable::class) - override fun evaluate() { - FlowManager.init(FlowConfig.Builder(RuntimeEnvironment.application) - .addDatabaseConfig(DatabaseConfig.Builder(TestDatabase::class.java) - .transactionManagerCreator(::ImmediateTransactionManager2) - .build()) - .addDatabaseConfig(DatabaseConfig.builder(ContentDatabase::class.java) - .databaseName("content") - .build()) - .build()) - try { - base.evaluate() - } finally { - FlowManager.destroy() - } - } - } - } - - companion object { - fun create() = DBFlowTestRule() - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/ImmediateTransactionManager.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/ImmediateTransactionManager.kt deleted file mode 100644 index ed8245ef7..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/ImmediateTransactionManager.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.raizlabs.android.dbflow - -import com.raizlabs.android.dbflow.config.DatabaseDefinition -import com.raizlabs.android.dbflow.runtime.BaseTransactionManager -import com.raizlabs.android.dbflow.structure.database.transaction.ITransactionQueue -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction - -/** - * Description: Executes all transactions on same thread for testing. - */ -class ImmediateTransactionManager2(databaseDefinition: DatabaseDefinition) - : BaseTransactionManager(ImmediateTransactionQueue2(), databaseDefinition) - - -class ImmediateTransactionQueue2 : ITransactionQueue { - - override fun add(transaction: Transaction) { - transaction.newBuilder() - .runCallbacksOnSameThread(true) - .build() - .executeSync() - } - - override fun cancel(transaction: Transaction) { - - } - - override fun startIfNotAlive() { - } - - override fun cancel(name: String) { - } - - override fun quit() { - } - -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/TestDatabase.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/TestDatabase.kt deleted file mode 100644 index 44f4860da..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/TestDatabase.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.raizlabs.android.dbflow - -import com.raizlabs.android.dbflow.annotation.Database -import com.raizlabs.android.dbflow.annotation.Migration -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.sql.migration.UpdateTableMigration - -/** - * Description: - */ -@Database(version = TestDatabase.VERSION) -object TestDatabase { - - const val VERSION = 1 - - @Migration(version = 1, database = TestDatabase::class) - class TestMigration : UpdateTableMigration(SimpleModel::class.java) { - override fun onPostMigrate() { - super.onPostMigrate() - } - } - -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/config/DatabaseConfigTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/config/DatabaseConfigTest.kt deleted file mode 100644 index 4ac3564ad..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/config/DatabaseConfigTest.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.raizlabs.android.dbflow.config - -import com.nhaarman.mockito_kotlin.mock -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.runtime.BaseTransactionManager -import com.raizlabs.android.dbflow.structure.database.DatabaseHelperListener -import com.raizlabs.android.dbflow.structure.database.OpenHelper -import com.raizlabs.android.dbflow.structure.database.transaction.ITransactionQueue -import org.junit.Assert -import org.junit.Before -import org.junit.Test - - -/** - * Description: - */ -class DatabaseConfigTest : BaseUnitTest() { - - private lateinit var builder: FlowConfig.Builder - - @Before - fun setup() { - FlowManager.reset() - FlowLog.setMinimumLoggingLevel(FlowLog.Level.V) - builder = FlowConfig.Builder(context) - } - - @Test - fun test_databaseConfig() { - - val helperListener = mock() - - val openHelperCreator = CustomOpenHelperCreator() - val managerCreator = CustomTransactionManagerCreator() - - FlowManager.init(builder - .addDatabaseConfig(DatabaseConfig.Builder(TestDatabase::class.java) - .databaseName("Test") - .helperListener(helperListener) - .openHelper(openHelperCreator) - .transactionManagerCreator(managerCreator) - .build()) - .build()) - - val flowConfig = FlowManager.getConfig() - Assert.assertNotNull(flowConfig) - - val databaseConfig = flowConfig.databaseConfigMap()[TestDatabase::class.java]!! - Assert.assertEquals("Test", databaseConfig.databaseName) - Assert.assertEquals(".db", databaseConfig.databaseExtensionName) - Assert.assertEquals(databaseConfig.transactionManagerCreator(), managerCreator) - Assert.assertEquals(databaseConfig.databaseClass(), TestDatabase::class.java) - Assert.assertEquals(databaseConfig.helperCreator(), openHelperCreator) - Assert.assertEquals(databaseConfig.helperListener(), helperListener) - Assert.assertTrue(databaseConfig.tableConfigMap().isEmpty()) - - - val databaseDefinition = FlowManager.getDatabase(TestDatabase::class.java) - Assert.assertEquals(databaseDefinition.transactionManager, - managerCreator.testTransactionManager) - Assert.assertEquals(databaseDefinition.helper, openHelperCreator.customOpenHelper) - } - - @Test - fun test_EmptyName() { - FlowManager.init(builder - .addDatabaseConfig(DatabaseConfig.Builder(TestDatabase::class.java) - .databaseName("Test") - .extensionName("") - .build()) - .build()) - - val databaseConfig = FlowManager.getConfig().databaseConfigMap()[TestDatabase::class.java]!! - Assert.assertEquals("Test", databaseConfig.databaseName) - Assert.assertEquals("", databaseConfig.databaseExtensionName) - } - - class CustomTransactionManagerCreator : DatabaseConfig.TransactionManagerCreator { - - lateinit var testTransactionManager: TestTransactionManager - - override fun createManager(databaseDefinition: DatabaseDefinition): BaseTransactionManager { - testTransactionManager = TestTransactionManager(databaseDefinition) - return testTransactionManager - } - } - - class CustomOpenHelperCreator : DatabaseConfig.OpenHelperCreator { - - val customOpenHelper = mock() - - override fun createHelper(databaseDefinition: DatabaseDefinition, helperListener: DatabaseHelperListener): OpenHelper { - return customOpenHelper - } - } - -} - -class TestTransactionManager(databaseDefinition: DatabaseDefinition) - : BaseTransactionManager(mock(), databaseDefinition) \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/ContentProviderObjects.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/ContentProviderObjects.kt deleted file mode 100644 index ccdedc49b..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/ContentProviderObjects.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.raizlabs.android.dbflow.contentprovider - -import com.raizlabs.android.dbflow.annotation.Column -import com.raizlabs.android.dbflow.annotation.Database -import com.raizlabs.android.dbflow.annotation.ForeignKey -import com.raizlabs.android.dbflow.annotation.ForeignKeyReference -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table -import com.raizlabs.android.dbflow.annotation.provider.ContentProvider -import com.raizlabs.android.dbflow.annotation.provider.ContentUri -import com.raizlabs.android.dbflow.annotation.provider.TableEndpoint -import com.raizlabs.android.dbflow.structure.provider.BaseProviderModel -import com.raizlabs.android.dbflow.structure.provider.BaseSyncableProviderModel -import com.raizlabs.android.dbflow.structure.provider.ContentUtils - - -/** - * Description: - */ -@ContentProvider(authority = ContentDatabase.AUTHORITY, database = ContentDatabase::class, - baseContentUri = ContentDatabase.BASE_CONTENT_URI) -@Database(version = ContentDatabase.VERSION) -object ContentDatabase { - - const val BASE_CONTENT_URI = "content://" - - const val AUTHORITY = "com.raizlabs.android.content.test.ContentDatabase" - - const val VERSION = 1 - -} - -@TableEndpoint(name = ContentProviderModel.NAME, contentProvider = ContentDatabase::class) -@Table(database = ContentDatabase::class, name = ContentProviderModel.NAME) -class ContentProviderModel(@PrimaryKey(autoincrement = true) - var id: Long = 0, - @Column - var notes: String? = null, - @Column - var title: String? = null) : BaseProviderModel() { - - override fun getDeleteUri() = TestContentProvider.ContentProviderModel.CONTENT_URI - - override fun getInsertUri() = TestContentProvider.ContentProviderModel.CONTENT_URI - - override fun getUpdateUri() = TestContentProvider.ContentProviderModel.CONTENT_URI - - override fun getQueryUri() = TestContentProvider.ContentProviderModel.CONTENT_URI - - companion object { - - const val NAME = "ContentProviderModel" - - @ContentUri(path = NAME, type = ContentUri.ContentType.VND_MULTIPLE + NAME) - val CONTENT_URI = ContentUtils.buildUriWithAuthority(ContentDatabase.AUTHORITY) - } -} - -@Table(database = ContentDatabase::class) -class NoteModel(@PrimaryKey(autoincrement = true) - var id: Long = 0, - - @ForeignKey(references = arrayOf(ForeignKeyReference(columnName = "providerModel", - foreignKeyColumnName = "id"))) - var contentProviderModel: ContentProviderModel? = null, - - @Column - var note: String? = null, - - @Column - var isOpen: Boolean = false) : BaseProviderModel() { - - override fun getDeleteUri() = TestContentProvider.NoteModel.CONTENT_URI - - override fun getInsertUri() = TestContentProvider.NoteModel.CONTENT_URI - - override fun getUpdateUri() = TestContentProvider.NoteModel.CONTENT_URI - - override fun getQueryUri() = TestContentProvider.NoteModel.CONTENT_URI -} - -@Table(database = ContentDatabase::class) -class TestSyncableModel(@PrimaryKey(autoincrement = true) - var id: Long = 0, - @Column - var name: String? = null) : BaseSyncableProviderModel() { - - override fun getDeleteUri() = TestContentProvider.TestSyncableModel.CONTENT_URI - - override fun getInsertUri() = TestContentProvider.TestSyncableModel.CONTENT_URI - - override fun getUpdateUri() = TestContentProvider.TestSyncableModel.CONTENT_URI - - override fun getQueryUri() = TestContentProvider.TestSyncableModel.CONTENT_URI -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/ContentProviderTests.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/ContentProviderTests.kt deleted file mode 100644 index a35431650..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/ContentProviderTests.kt +++ /dev/null @@ -1,127 +0,0 @@ -package com.raizlabs.android.dbflow.contentprovider - -import android.content.ContentResolver -import android.content.pm.ProviderInfo -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.result -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.where -import com.raizlabs.android.dbflow.sql.language.Delete -import com.raizlabs.android.dbflow.structure.provider.ContentUtils -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.robolectric.Robolectric -import org.robolectric.RuntimeEnvironment - -/** - * Description: - */ -class ContentProviderTests : BaseUnitTest() { - private val mockContentResolver: ContentResolver - get() = RuntimeEnvironment.application.contentResolver - - @Before - fun setUp() { - val info = ProviderInfo() - info.authority = TestContentProvider.AUTHORITY - Robolectric.buildContentProvider(TestContentProvider_Provider::class.java).create(info) - } - - @Test - fun testContentProviderUtils() { - Delete.tables(NoteModel::class.java, ContentProviderModel::class.java) - - val contentProviderModel = ContentProviderModel() - contentProviderModel.notes = "Test" - var uri = ContentUtils.insert(mockContentResolver, TestContentProvider.ContentProviderModel.CONTENT_URI, contentProviderModel) - assertEquals(TestContentProvider.ContentProviderModel.CONTENT_URI.toString() + "/" + contentProviderModel.id, uri.toString()) - assertTrue(contentProviderModel.exists()) - - contentProviderModel.notes = "NewTest" - val update = ContentUtils.update(mockContentResolver, TestContentProvider.ContentProviderModel.CONTENT_URI, contentProviderModel) - assertEquals(update.toLong(), 1) - assertTrue(contentProviderModel.exists()) - contentProviderModel.load() - assertEquals("NewTest", contentProviderModel.notes) - - val noteModel = NoteModel() - noteModel.note = "Test" - noteModel.contentProviderModel = contentProviderModel - uri = ContentUtils.insert(mockContentResolver, TestContentProvider.NoteModel.CONTENT_URI, noteModel) - assertEquals(TestContentProvider.NoteModel.CONTENT_URI.toString() + "/" + noteModel.id, uri.toString()) - assertTrue(noteModel.exists()) - - assertTrue(ContentUtils.delete(mockContentResolver, TestContentProvider.NoteModel.CONTENT_URI, noteModel) > 0) - assertTrue(!noteModel.exists()) - - assertTrue(ContentUtils.delete(mockContentResolver, TestContentProvider.ContentProviderModel.CONTENT_URI, contentProviderModel) > 0) - assertTrue(!contentProviderModel.exists()) - - Delete.tables(NoteModel::class.java, ContentProviderModel::class.java) - } - - @Test - fun testContentProviderNative() { - Delete.tables(NoteModel::class.java, ContentProviderModel::class.java) - - val contentProviderModel = ContentProviderModel(notes = "Test") - contentProviderModel.insert() - assertTrue(contentProviderModel.exists()) - - contentProviderModel.notes = "NewTest" - contentProviderModel.update() - contentProviderModel.load() - assertEquals("NewTest", contentProviderModel.notes) - - val noteModel = NoteModel(note = "Test", contentProviderModel = contentProviderModel) - noteModel.insert() - - noteModel.note = "NewTest" - noteModel.update() - noteModel.load() - assertEquals("NewTest", noteModel.note) - - assertTrue(noteModel.exists()) - - noteModel.delete() - assertTrue(!noteModel.exists()) - - contentProviderModel.delete() - assertTrue(!contentProviderModel.exists()) - - Delete.tables(NoteModel::class.java, ContentProviderModel::class.java) - } - - @Test - fun testSyncableModel() { - Delete.table(TestSyncableModel::class.java) - - var testSyncableModel = TestSyncableModel(name = "Name") - testSyncableModel.save() - - assertTrue(testSyncableModel.exists()) - - testSyncableModel.name = "TestName" - testSyncableModel.update() - assertEquals(testSyncableModel.name, "TestName") - - testSyncableModel = (select from TestSyncableModel::class - where (TestSyncableModel_Table.id.`is`(testSyncableModel.id))).result!! - - val fromContentProvider = TestSyncableModel(id = testSyncableModel.id) - fromContentProvider.load() - - assertEquals(fromContentProvider.name, testSyncableModel.name) - assertEquals(fromContentProvider.id, testSyncableModel.id) - - testSyncableModel.delete() - assertFalse(testSyncableModel.exists()) - - Delete.table(TestSyncableModel::class.java) - } - -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/RealContentProvider.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/RealContentProvider.kt deleted file mode 100644 index ad4ae5a3d..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/contentprovider/RealContentProvider.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.raizlabs.android.dbflow.contentprovider - -import android.content.ContentProvider -import android.content.ContentValues -import android.database.Cursor -import android.net.Uri -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.config.FlowConfig -import com.raizlabs.android.dbflow.config.FlowManager -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper - -class RealContentProvider : ContentProvider() { - - lateinit var database: DatabaseWrapper - - override fun onCreate(): Boolean { - FlowManager.init(FlowConfig.Builder(context).build()) - database = FlowManager.getDatabase(TestDatabase::class.java).writableDatabase - return true - } - - override fun query(uri: Uri, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?): Cursor? { - return null - } - - override fun getType(uri: Uri): String? { - return null - } - - override fun insert(uri: Uri, values: ContentValues?): Uri? { - return null - } - - override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { - return 0 - } - - override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int { - return 0 - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/database/transaction/FastStoreModelTransactionTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/database/transaction/FastStoreModelTransactionTest.kt deleted file mode 100644 index ec50b25b7..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/database/transaction/FastStoreModelTransactionTest.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.raizlabs.android.dbflow.database.transaction - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.kotlinextensions.database -import com.raizlabs.android.dbflow.kotlinextensions.fastInsert -import com.raizlabs.android.dbflow.kotlinextensions.fastSave -import com.raizlabs.android.dbflow.kotlinextensions.fastUpdate -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.list -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.TwoColumnModel -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotEquals -import org.junit.Test -import java.util.* - -class FastStoreModelTransactionTest : BaseUnitTest() { - - @Test - fun testSaveBuilder() { - - database() - .beginTransactionAsync((0..9) - .map { SimpleModel("$it") } - .fastSave().build()) - .execute() - - val list = (select from SimpleModel::class).list - assertEquals(10, list.size) - } - - @Test - fun testInsertBuilder() { - - database() - .beginTransactionAsync((0..9) - .map { SimpleModel("$it") } - .fastInsert().build()) - .execute() - - val list = (select from SimpleModel::class).list - assertEquals(10, list.size) - } - - @Test - fun testUpdateBuilder() { - - val oldList = (0..9).map { TwoColumnModel("$it", Random().nextInt()) } - database() - .beginTransactionAsync(oldList.fastInsert().build()) - .execute() - - database() - .beginTransactionAsync((0..9).map { TwoColumnModel("$it", Random().nextInt()) } - .fastUpdate().build()) - .execute() - - val list = (select from TwoColumnModel::class).list - assertEquals(10, list.size) - list.forEachIndexed { index, model -> - assertNotEquals(model, oldList[index]) - } - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowCursorIteratorTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowCursorIteratorTest.kt deleted file mode 100644 index db50dcab2..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowCursorIteratorTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.raizlabs.android.dbflow.list - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Description: - */ -class FlowCursorIteratorTest : BaseUnitTest() { - - - @Test - fun testCanIterateFullList() { - (0..9).forEach { - SimpleModel("$it").save() - } - - - var count = 0 - (select from SimpleModel::class).cursorList().iterator().forEach { - assertEquals("$count", it.name) - count++ - } - - } - - @Test - fun testCanIteratePartialList() { - (0..9).forEach { - SimpleModel("$it").save() - } - - var count = 2 - (select from SimpleModel::class).cursorList().iterator(2, 7).forEach { - assertEquals("$count", it.name) - count++ - } - assertEquals(7, count) - } - - @Test - fun testCanSupplyBadMaximumValue() { - (0..9).forEach { - SimpleModel("$it").save() - } - - var count = 2 - (select from SimpleModel::class).cursorList().iterator(2, Long.MAX_VALUE).forEach { - assertEquals("$count", it.name) - count++ - } - assertEquals(8, count) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowCursorListTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowCursorListTest.kt deleted file mode 100644 index 301fcec18..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowCursorListTest.kt +++ /dev/null @@ -1,120 +0,0 @@ -package com.raizlabs.android.dbflow.list - -import com.nhaarman.mockito_kotlin.mock -import com.nhaarman.mockito_kotlin.times -import com.nhaarman.mockito_kotlin.verify -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.cursor -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.get -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.structure.cache.SimpleMapCache -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNotEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -/** - * Description: - */ -class FlowCursorListTest : BaseUnitTest() { - - - @Test - fun validateBuilder() { - - val list = FlowCursorList.Builder(select from SimpleModel::class) - .modelCache(SimpleMapCache(55)) - .build() - - assertTrue(list.modelCache() is SimpleMapCache<*>) - assertTrue(list.cachingEnabled()) - } - - @Test - fun validateNonCachedBuilder() { - - val list = FlowCursorList.Builder(select from SimpleModel::class) - .cacheModels(false) - .build() - - assertFalse(list.cachingEnabled()) - } - - @Test - fun validateCursorPassed() { - val cursor = (select from SimpleModel::class).cursor - val list = FlowCursorList.Builder(SimpleModel::class.java) - .cursor(cursor) - .build() - - assertEquals(cursor, list.cursor()) - } - - @Test - fun validateModelQueriable() { - val modelQueriable = (select from SimpleModel::class) - val list = FlowCursorList.Builder(SimpleModel::class.java) - .modelQueriable(modelQueriable) - .build() - - assertEquals(modelQueriable, list.modelQueriable()) - } - - @Test - fun validateSpecialModelCache() { - (0..9).forEach { - SimpleModel("$it").save() - } - - val list = (select from SimpleModel::class).cursorList() - assertEquals(10, list.count) - val firsItem = list[0] - assertEquals(firsItem, firsItem) - assertEquals(list[2], list[2]) - - list.clearCache() - assertNotEquals(firsItem, list[0]) - } - - @Test - fun validateGetAll() { - (0..9).forEach { - SimpleModel("$it").save() - } - - val list = (select from SimpleModel::class).cursorList() - val all = list.all - assertEquals(list.count, all.size.toLong()) - all.indices.forEach { - assertEquals(all[it], list[it]) - } - } - - @Test - fun validateCursorChange() { - (0..9).forEach { - SimpleModel("$it").save() - } - - val list = (select from SimpleModel::class).cursorList() - - val listener = mock>() - list.addOnCursorRefreshListener(listener) - assertEquals(10, list.count) - SimpleModel("10").save() - list.refresh() - assertEquals(11, list.count) - - verify(listener).onCursorRefreshed(list) - - list.removeOnCursorRefreshListener(listener) - - list.refresh() - verify(listener, times(1)).onCursorRefreshed(list) - } -} - diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowQueryListTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowQueryListTest.kt deleted file mode 100644 index 49680b476..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/list/FlowQueryListTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.raizlabs.android.dbflow.list - -import com.nhaarman.mockito_kotlin.argumentCaptor -import com.nhaarman.mockito_kotlin.mock -import com.nhaarman.mockito_kotlin.verify -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.structure.cache.SimpleMapCache -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test - -class FlowQueryListTest : BaseUnitTest() { - - @Test - fun validateBuilder() { - - val list = FlowQueryList.Builder(select from SimpleModel::class) - .modelCache(SimpleMapCache(55)) - .transact(true) - .changeInTransaction(true) - .build() - - assertTrue(list.cursorList().modelCache() is SimpleMapCache<*>) - assertTrue(list.cursorList().cachingEnabled()) - assertTrue(list.transact()) - assertTrue(list.changeInTransaction()) - } - - @Test - fun validateNonCachedBuilder() { - - val list = FlowQueryList.Builder(select from SimpleModel::class) - .cacheModels(false) - .build() - - assertFalse(list.cursorList().cachingEnabled()) - - } - - - @Test - fun validateListOperations() { - val mockSuccess = mock() - val mockError = mock() - val list = (select from SimpleModel::class).flowQueryList() - .newBuilder().success(mockSuccess) - .error(mockError) - .build() - list += SimpleModel("1") - - // verify added - assertEquals(1, list.count) - assertFalse(list.isEmpty()) - - // verify success called - verify(mockSuccess).onSuccess(argumentCaptor().capture()) - - list -= SimpleModel("1") - assertEquals(0, list.count) - - list += SimpleModel("1") - list.removeAt(0) - assertEquals(0, list.count) - - val elements = arrayListOf(SimpleModel("1"), SimpleModel("2")) - list.addAll(elements) - assertEquals(2, list.count) - list.removeAll(elements) - assertEquals(0, list.count) - - list.addAll(elements) - - val typedArray = list.toTypedArray() - assertEquals(typedArray.size, list.size) - - list.clear() - assertEquals(0, list.size) - } - -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/DontCreateModelTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/DontCreateModelTest.kt deleted file mode 100644 index 22edf3655..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/DontCreateModelTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.raizlabs.android.dbflow.models - -import android.database.sqlite.SQLiteException -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.assertThrowsException -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.list -import com.raizlabs.android.dbflow.kotlinextensions.select -import org.junit.Test - -/** - * Description: - */ -class DontCreateModelTest : BaseUnitTest() { - - @Test - fun testModelNotCreated() { - assertThrowsException(SQLiteException::class) { - (select from DontCreateModel::class).list - } - } -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/InnerClassExample.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/InnerClassExample.kt deleted file mode 100644 index ee120108d..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/InnerClassExample.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.raizlabs.android.dbflow.models - -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table - -/** - * Example ensuring static inner classes work. - */ -class Outer { - - @Table(database = TestDatabase::class) - class Inner(@PrimaryKey var id: Int = 0) -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ModelViewTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ModelViewTest.kt deleted file mode 100644 index a1f58630b..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ModelViewTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.models - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.models.java.JavaModelView -import org.junit.Test - -class ModelViewTest : BaseUnitTest() { - - @Test - fun validateModelViewQuery() { - assertEquals("SELECT `id` AS `authorId`,`first_name` || ' ' || `last_name` AS `authorName` FROM `Author`", - AuthorView.query) - } - - @Test - fun validateJavaModelViewQuery() { - assertEquals("SELECT `first_name` AS `firstName`,`id` AS `id`", JavaModelView.QUERY) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ModelViews.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ModelViews.kt deleted file mode 100644 index 2fa430801..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ModelViews.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.raizlabs.android.dbflow.models - -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.annotation.Column -import com.raizlabs.android.dbflow.annotation.ColumnMap -import com.raizlabs.android.dbflow.annotation.ModelView -import com.raizlabs.android.dbflow.annotation.ModelViewQuery -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.property -import com.raizlabs.android.dbflow.models.Author_Table.first_name -import com.raizlabs.android.dbflow.models.Author_Table.id -import com.raizlabs.android.dbflow.models.Author_Table.last_name -import com.raizlabs.android.dbflow.sql.language.SQLite.select -import com.raizlabs.android.dbflow.sql.language.property.IProperty - -class AuthorName(var name: String = "", var age: Int = 0) - - -@ModelView(database = TestDatabase::class) -class AuthorView(@Column var authorId: Int = 0, @Column var authorName: String = "", - @ColumnMap var author: AuthorName? = null) { - - companion object { - @JvmField - @ModelViewQuery - val query = select(id.`as`("authorId"), - first_name.concatenate(" ".property as IProperty>) - .concatenate(last_name as IProperty>).`as`("authorName")) from Author::class - } -} - -@ModelView(database = TestDatabase::class, priority = 2, allFields = true) -class PriorityView(var name: String = "") { - - companion object { - @JvmField - @ModelViewQuery - val query = select((first_name + last_name).`as`("name")) from Author::class - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/NonTypical/nonTypicalClassName.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/NonTypical/nonTypicalClassName.kt deleted file mode 100644 index aa38949fb..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/NonTypical/nonTypicalClassName.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.raizlabs.android.dbflow.models.NonTypical - -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table - -/** - * Tests package name capitalized, class name is lower cased. - */ -@Table(database = TestDatabase::class) -class nonTypicalClassName(@PrimaryKey var id: Int = 0) \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt deleted file mode 100644 index 188869dc6..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModelTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.raizlabs.android.dbflow.models - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.delete -import com.raizlabs.android.dbflow.kotlinextensions.exists -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.list -import com.raizlabs.android.dbflow.kotlinextensions.result -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.writableDatabaseForTable -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Test - -class OneToManyModelTest : BaseUnitTest() { - - @Test - fun testOneToManyModel() { - - var testModel2 = TwoColumnModel("Greater", 4) - testModel2.save() - - testModel2 = TwoColumnModel("Lesser", 1) - testModel2.save() - - // assert we save - var oneToManyModel = OneToManyModel("HasOrders") - oneToManyModel.save() - assertTrue(oneToManyModel.exists()) - - // assert loading works as expected. - oneToManyModel = (select from OneToManyModel::class).result!! - assertNotNull(oneToManyModel.getRelatedOrders()) - assertTrue(!oneToManyModel.getRelatedOrders().isEmpty()) - - // assert the deletion cleared the variable - oneToManyModel.delete() - assertFalse(oneToManyModel.exists()) - assertNull(oneToManyModel.orders) - - // assert singular relationship was deleted. - val list = (select from TwoColumnModel::class).list - assertTrue(list.size == 1) - - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt deleted file mode 100644 index 18864840b..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/OneToManyModels.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.raizlabs.android.dbflow.models - -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.annotation.OneToMany -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.oneToMany -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.where -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.structure.BaseModel - -@Table(database = TestDatabase::class) -class OneToManyModel(@PrimaryKey var name: String? = null) { - - var orders: List? = null - - var models: List? = null - - @get:OneToMany(methods = arrayOf(OneToMany.Method.ALL)) - var simpleModels by oneToMany { select from OneToManyBaseModel::class } - - @OneToMany(methods = arrayOf(OneToMany.Method.ALL), isVariablePrivate = true, - variableName = "orders", efficientMethods = false) - fun getRelatedOrders(): List { - var localOrders = orders - if (localOrders == null) { - localOrders = (select from TwoColumnModel::class where id.greaterThan(3)) - .queryList() - } - orders = localOrders - return localOrders - } - - @OneToMany(methods = arrayOf(OneToMany.Method.DELETE), isVariablePrivate = true, - variableName = "models") - fun getRelatedModels(): List { - var localModels = models - if (localModels == null) { - localModels = (select from OneToManyBaseModel::class).queryList() - } - models = localModels - return localModels - } - - -} - -@Table(database = TestDatabase::class) -class OneToManyBaseModel(@PrimaryKey var id: Int = 0) : BaseModel() \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModelTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModelTest.kt deleted file mode 100644 index dcee6a3be..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/QueryModelTest.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.raizlabs.android.dbflow.models - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.eq -import com.raizlabs.android.dbflow.kotlinextensions.exists -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.innerJoin -import com.raizlabs.android.dbflow.kotlinextensions.on -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.models.Author_Table.id -import com.raizlabs.android.dbflow.models.Blog_Table.author_id -import com.raizlabs.android.dbflow.models.Blog_Table.name -import com.raizlabs.android.dbflow.sql.language.SQLite.select -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Description: Tests to ensure we can load a Query model from the DB - */ -class QueryModelTest : BaseUnitTest() { - - - @Test - fun testCanLoadAuthorBlogs() { - val author = Author(0, "Andrew", "Grosner") - author.save() - val blog = Blog(0, "My First Blog", author) - blog.save() - - assert(author.exists()) - assert(blog.exists()) - - val result = (select(name.withTable().`as`("blogName"), id.withTable().`as`("authorId"), - Blog_Table.id.withTable().`as`("blogId")) from Blog::class innerJoin - Author::class on (author_id.withTable() eq id.withTable())) - .queryCustomSingle(AuthorNameQuery::class.java)!! - assertEquals(author.id, result.authorId) - assertEquals(blog.id, result.blogId) - } -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ants/AntHill.java b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ants/AntHill.java deleted file mode 100644 index b99544b40..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ants/AntHill.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.raizlabs.android.dbflow.models.ants; - -import com.raizlabs.android.dbflow.TestDatabase; -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.structure.BaseModel; - -@Table(database = TestDatabase.class) -public class AntHill extends BaseModel { - @PrimaryKey - public String hillId; -} - diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ants/Ants.java b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ants/Ants.java deleted file mode 100644 index e503d1226..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/ants/Ants.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.models.ants; - -import com.raizlabs.android.dbflow.TestDatabase; -import com.raizlabs.android.dbflow.annotation.ForeignKey; -import com.raizlabs.android.dbflow.annotation.ForeignKeyReference; -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.structure.BaseModel; - -@Table(database = TestDatabase.class) -public class Ants extends BaseModel { - @PrimaryKey - public String antId; - - @PrimaryKey - @ForeignKey(tableClass = AntHill.class, references = { - @ForeignKeyReference(columnName = "hillIdRef", foreignKeyColumnName = "hillId") - }) - public String hillId; -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/Issue.java b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/Issue.java deleted file mode 100644 index 61d6663d6..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/Issue.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.raizlabs.android.dbflow.models.issue; - -import com.raizlabs.android.dbflow.TestDatabase; -import com.raizlabs.android.dbflow.annotation.OneToMany; -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.structure.BaseModel; - -import java.util.List; - -/** - * Description: - */ - -@Table(database = TestDatabase.class) -public class Issue extends BaseModel { - - @PrimaryKey - String id; - - List subIssueList; - - @OneToMany(methods = {OneToMany.Method.SAVE, OneToMany.Method.DELETE}, variableName = "subIssueList") - public List getDbSubIssueList() { - if (subIssueList == null || subIssueList.isEmpty()) { - subIssueList = SQLite.select() - .from(SubIssue.class) - .where(SubIssue_Table.owningIssueId.eq(id)) - .queryList(); - } - return subIssueList; - } -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/Page.java b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/Page.java deleted file mode 100644 index 3f93879ef..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/Page.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.raizlabs.android.dbflow.models.issue; - -import com.raizlabs.android.dbflow.TestDatabase; -import com.raizlabs.android.dbflow.annotation.Column; -import com.raizlabs.android.dbflow.annotation.ForeignKey; -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.structure.BaseModel; - -/** - * Description: - */ -@Table(database = TestDatabase.class) -public class Page extends BaseModel { - - @PrimaryKey - @Column - String id; - - @PrimaryKey - String owningIssueId; - - @ForeignKey(stubbedRelationship = true) - SubIssue subIssue; -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/SubIssue.java b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/SubIssue.java deleted file mode 100644 index 1841d4750..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/issue/SubIssue.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.raizlabs.android.dbflow.models.issue; - -import com.raizlabs.android.dbflow.TestDatabase; -import com.raizlabs.android.dbflow.annotation.OneToMany; -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.structure.BaseModel; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: - */ - -@Table(database = TestDatabase.class) -public class SubIssue extends BaseModel { - - @PrimaryKey - String id; - - @PrimaryKey - String owningIssueId; - - List pageList; - - @OneToMany(methods = {OneToMany.Method.SAVE, OneToMany.Method.DELETE}, variableName = "pageList") - public List getDbPageList() { - if (pageList == null) { - pageList = new ArrayList<>(); - } - if (pageList.isEmpty()) { - pageList = SQLite.select() - .from(Page.class) - .where(Page_Table.owningIssueId.eq(owningIssueId), Page_Table.subIssue_id.eq(id)) - .queryList(); - } - return pageList; - } -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/java/JavaModelView.java b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/java/JavaModelView.java deleted file mode 100644 index 7e2f2860c..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/java/JavaModelView.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.raizlabs.android.dbflow.models.java; - -import com.raizlabs.android.dbflow.TestDatabase; -import com.raizlabs.android.dbflow.annotation.Column; -import com.raizlabs.android.dbflow.annotation.ModelView; -import com.raizlabs.android.dbflow.annotation.ModelViewQuery; -import com.raizlabs.android.dbflow.models.Author_Table; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.language.SQLite; - -@ModelView(database = TestDatabase.class) -public class JavaModelView { - - @ModelViewQuery - public static final Query QUERY = getQuery(); - - @Column - String id; - - @Column - Integer firstName; - - private static Query getQuery() { - return SQLite.select(Author_Table.first_name.as("firstName"), Author_Table.id.as("id")); - } -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/java/otherpackage/ExampleModel.java b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/java/otherpackage/ExampleModel.java deleted file mode 100644 index bac84d390..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/models/java/otherpackage/ExampleModel.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.raizlabs.android.dbflow.models.java.otherpackage; - -import com.raizlabs.android.dbflow.TestDatabase; -import com.raizlabs.android.dbflow.annotation.Column; -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.models.java.DatabaseModel; - -@Table(database = TestDatabase.class) -public class ExampleModel extends DatabaseModel { - @Column - String name; -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/runtime/DirectNotifierTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/runtime/DirectNotifierTest.kt deleted file mode 100644 index 34834cca5..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/runtime/DirectNotifierTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.raizlabs.android.dbflow.runtime - -import android.content.Context -import com.nhaarman.mockito_kotlin.mock -import com.nhaarman.mockito_kotlin.times -import com.nhaarman.mockito_kotlin.verify -import com.raizlabs.android.dbflow.ImmediateTransactionManager2 -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.config.DatabaseConfig -import com.raizlabs.android.dbflow.config.FlowConfig -import com.raizlabs.android.dbflow.config.FlowManager -import com.raizlabs.android.dbflow.kotlinextensions.columnValues -import com.raizlabs.android.dbflow.kotlinextensions.delete -import com.raizlabs.android.dbflow.kotlinextensions.insert -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.set -import com.raizlabs.android.dbflow.kotlinextensions.update -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import com.raizlabs.android.dbflow.structure.BaseModel -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito -import org.robolectric.RobolectricTestRunner -import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config - -@RunWith(RobolectricTestRunner::class) -@Config (manifest = Config.NONE) -class DirectNotifierTest { - - val context: Context - get() = RuntimeEnvironment.application - - @Before - fun setupTest() { - FlowManager.init(FlowConfig.Builder(context) - .addDatabaseConfig(DatabaseConfig.Builder(TestDatabase::class.java) - .transactionManagerCreator(::ImmediateTransactionManager2) - .modelNotifier(DirectModelNotifier.get()) - .build()).build()) - } - - @Test - fun validateCanNotifyDirect() { - val simpleModel = SimpleModel("Name") - - val modelChange = mock>() - DirectModelNotifier.get().registerForModelStateChanges(SimpleModel::class.java, modelChange) - - simpleModel.insert() - verify(modelChange).onModelChanged(simpleModel, BaseModel.Action.INSERT) - - simpleModel.update() - verify(modelChange).onModelChanged(simpleModel, BaseModel.Action.UPDATE) - - simpleModel.save() - verify(modelChange, times(2)).onModelChanged(simpleModel, BaseModel.Action.UPDATE) - - simpleModel.delete() - verify(modelChange).onModelChanged(simpleModel, BaseModel.Action.DELETE) - } - - @Test - fun validateCanNotifyWrapperClasses() { - val modelChange = Mockito.mock(OnTableChangedListener::class.java) - DirectModelNotifier.get().registerForTableChanges(SimpleModel::class.java, modelChange) - - insert().columnValues(SimpleModel_Table.name to "name").executeInsert() - - verify(modelChange).onTableChanged(SimpleModel::class.java, BaseModel.Action.INSERT) - - (update() set SimpleModel_Table.name.eq("name2")).executeUpdateDelete() - - verify(modelChange).onTableChanged(SimpleModel::class.java, BaseModel.Action.UPDATE) - - delete().executeUpdateDelete() - - verify(modelChange).onTableChanged(SimpleModel::class.java, BaseModel.Action.DELETE) - } - - @After - fun teardown() { - FlowManager.destroy() - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/CursorResultSubscriberTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/CursorResultSubscriberTest.kt deleted file mode 100644 index 0c81a6caf..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/CursorResultSubscriberTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.rx.kotlinextensions.rx -import org.junit.Assert.assertEquals -import org.junit.Test - -class CursorResultSubscriberTest : BaseUnitTest() { - - - @Test - fun testCanQueryStreamResults() { - (0..9).forEach { SimpleModel("$it").save() } - - var count = 0 - (select from SimpleModel::class).rx() - .queryStreamResults() - .toBlocking() - .subscribe { - count++ - assert(it != null) - } - - assertEquals(10, count) - } - -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/RXQueryTests.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/RXQueryTests.kt deleted file mode 100644 index c3c1ef240..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/RXQueryTests.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language - -import android.database.Cursor -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.insert -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table.name -import com.raizlabs.android.dbflow.rx.kotlinextensions.rx -import com.raizlabs.android.dbflow.rx.kotlinextensions.rxBaseQueriable -import com.raizlabs.android.dbflow.sql.language.SQLite.selectCountOf -import com.raizlabs.android.dbflow.sql.language.property.Property -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Test - -class RXQueryTests : BaseUnitTest() { - - @Test - fun testCanQuery() { - SimpleModel("Name").save() - - var cursor: Cursor? = null - (select from SimpleModel::class).rx() - .query() - .subscribe { - cursor = it - } - - assertEquals(1, cursor!!.count) - cursor!!.close() - } - - @Test - fun testCanCompileStatement() { - var databaseStatement: DatabaseStatement? = null - (insert().columnValues(name.`is`("name"))) - .rxBaseQueriable().compileStatement() - .subscribe { - databaseStatement = it - } - assertNotNull(databaseStatement) - databaseStatement!!.close() - } - - @Test - fun testCountMethod() { - SimpleModel("name").save() - SimpleModel("name2").save() - var count = 0L - (selectCountOf(Property.ALL_PROPERTY) from SimpleModel::class).rx() - .count().subscribe { - count = it - } - - assertEquals(2, count) - } - - @Test - fun testInsertMethod() { - var count = 0L - (insert().columnValues(name.eq("name"))) - .rxBaseQueriable() - .executeInsert() - .subscribe { - count = it - } - - assertEquals(1, count) - } - - @Test - fun testExecuteUpdateDelete() { - - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/RxModels.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/RxModels.kt deleted file mode 100644 index e2ea6dfe6..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/rx/language/RxModels.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.raizlabs.android.dbflow.rx.language - -import com.raizlabs.android.dbflow.TestDatabase -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table -import com.raizlabs.android.dbflow.rx.structure.BaseRXModel - - -@Table(database = TestDatabase::class, allFields = true) -class SimpleRXModel(@PrimaryKey var id: String = "") : BaseRXModel() - -@Table(database = TestDatabase::class) -class SimpleRXModel2(@PrimaryKey var id: String = "") : com.raizlabs.android.dbflow.rx2.structure.BaseRXModel() \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/CaseTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/CaseTest.kt deleted file mode 100644 index 1b29e968e..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/CaseTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.`else` -import com.raizlabs.android.dbflow.kotlinextensions.case -import com.raizlabs.android.dbflow.kotlinextensions.caseWhen -import com.raizlabs.android.dbflow.kotlinextensions.propertyString -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test - -class CaseTest : BaseUnitTest() { - - @Test - fun simpleCaseTest() { - val case = case(propertyString("country")) - .`when`("USA") - .then("Domestic") - .`else`("Foreign") - assertEquals("CASE country WHEN 'USA' THEN 'Domestic' ELSE 'Foreign' END `Country`", - case.end("Country").query.trim()) - assertTrue(case.isEfficientCase) - } - - @Test - fun searchedCaseTest() { - val case = caseWhen(SimpleModel_Table.name.eq("USA")).then("Domestic") - .`when`(SimpleModel_Table.name.eq("CA")).then("Canada") - .`else`("Foreign") - assertEquals("CASE WHEN `name`='USA' THEN 'Domestic' WHEN `name`='CA' THEN 'Canada' ELSE 'Foreign'", - case.query.trim()) - assertFalse(case.isEfficientCase) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/CursorResultTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/CursorResultTest.kt deleted file mode 100644 index b695dc7d4..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/CursorResultTest.kt +++ /dev/null @@ -1,128 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import android.database.StaleDataException -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.cursorResult -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.toCustomList -import com.raizlabs.android.dbflow.kotlinextensions.toCustomListClose -import com.raizlabs.android.dbflow.kotlinextensions.toCustomModel -import com.raizlabs.android.dbflow.kotlinextensions.toCustomModelClose -import com.raizlabs.android.dbflow.models.SimpleCustomModel -import com.raizlabs.android.dbflow.models.SimpleModel -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test - -class CursorResultTest : BaseUnitTest() { - - lateinit var result: CursorResult - - @Before - fun prepareList() { - (0..9).forEach { SimpleModel("$it").save() } - result = (select from SimpleModel::class).cursorResult - } - - @Test - fun validateToList() { - val list = result.toList() - assertEquals(10, list.size) - } - - @Test - fun validateToListClose() { - val list = result.toListClose() - assertEquals(10, list.size) - var exception = false - try { - result.toListClose() - } catch (i: Exception) { - when (i) { - is IllegalStateException, is StaleDataException -> exception = true - else -> throw i - } - } - assertTrue(exception) - } - - @Test - fun validateToCustomList() { - val list = result.toCustomList() - assertEquals(10, list.size) - } - - @Test - fun validateToCustomListClose() { - val list = result.toCustomListClose() - assertEquals(10, list.size) - var exception = false - try { - result.toCustomListClose() - } catch (i: Exception) { - when (i) { - is IllegalStateException, is StaleDataException -> exception = true - else -> throw i - } - } - assertTrue(exception) - } - - @Test - fun validateToModel() { - val model = result.toModel() - assertNotNull(model) - } - - @Test - fun validateToModelClose() { - val model = result.toModelClose() - assertNotNull(model) - var exception = false - try { - result.toModelClose() - } catch (i: Exception) { - when (i) { - is IllegalStateException, is StaleDataException -> exception = true - else -> throw i - } - } - assertTrue(exception) - } - - @Test - fun validateToCustomModel() { - val model = result.toCustomModel() - assertNotNull(model) - } - - @Test - fun validateToCustomModelClose() { - val model = result.toCustomModelClose() - assertNotNull(model) - var exception = false - try { - result.toCustomModelClose() - } catch (i: Exception) { - when (i) { - is IllegalStateException, is StaleDataException -> exception = true - else -> throw i - } - } - assertTrue(exception) - } - - @Test - fun validateNullCursor() { - result.swapCursor(null) - - assertTrue(result.toList().isEmpty()) - assertTrue(result.toCustomList().isEmpty()) - assertTrue(result.toModel() == null) - assertTrue(result.toCustomModel() == null) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/DeleteTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/DeleteTest.kt deleted file mode 100644 index b416ddf67..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/DeleteTest.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.delete -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.list -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Test - -class DeleteTest : BaseUnitTest() { - - @Test - fun validateQuery() { - assertEquals("DELETE ", Delete().query) - } - - @Test - fun validateDeletion() { - SimpleModel("name").save() - delete().execute() - assertFalse((select from SimpleModel::class).hasData()) - } - - @Test - fun validateDeletionWithQuery() { - SimpleModel("name").save() - SimpleModel("another name").save() - - val where = delete().where(SimpleModel_Table.name.`is`("name")) - assertEquals("DELETE FROM `SimpleModel` WHERE `name`='name'", where.query.trim()) - where.execute() - - assertEquals(1, (select from SimpleModel::class).list.size) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/ExistenceOperatorTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/ExistenceOperatorTest.kt deleted file mode 100644 index f4e410180..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/ExistenceOperatorTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.where -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import org.junit.Assert.assertEquals -import org.junit.Test - -class ExistenceOperatorTest : BaseUnitTest() { - - - @Test - fun validateQuery() { - assertEquals("EXISTS (SELECT * FROM `SimpleModel` WHERE `name`='name')", ExistenceOperator() - .where(select from SimpleModel::class where SimpleModel_Table.name.eq("name")).query.trim()) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/IndexTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/IndexTest.kt deleted file mode 100644 index 0dc98776b..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/IndexTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.indexOn -import com.raizlabs.android.dbflow.kotlinextensions.nameAlias -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import org.junit.Assert.assertEquals -import org.junit.Test - -class IndexTest : BaseUnitTest() { - - @Test - fun validateBasicIndex() { - assertEquals("CREATE INDEX IF NOT EXISTS `index` ON `SimpleModel`(`name`)", - indexOn("index", SimpleModel_Table.name).query) - } - - @Test - fun validateUniqueIndex() { - assertEquals("CREATE UNIQUE INDEX IF NOT EXISTS `index` ON `SimpleModel`(`name`, `test`)", - indexOn("index").unique(true).and(SimpleModel_Table.name) - .and("test".nameAlias).query) - } - - @Test - fun validateBasicIndexNameAlias() { - assertEquals("CREATE INDEX IF NOT EXISTS `index` ON `SimpleModel`(`name`, `test`)", - indexOn("index", "name".nameAlias, "test".nameAlias).query) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/IndexedByTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/IndexedByTest.kt deleted file mode 100644 index 77a6a74fc..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/IndexedByTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import com.raizlabs.android.dbflow.sql.language.property.IndexProperty -import org.junit.Assert.assertEquals -import org.junit.Test - -class IndexedByTest : BaseUnitTest() { - - @Test - fun validateQuery() { - val indexed = (select from SimpleModel::class) - .indexedBy(IndexProperty("Index", false, SimpleModel::class.java, SimpleModel_Table.name)) - assertEquals("SELECT * FROM `SimpleModel` INDEXED BY `Index`", indexed.query.trim()) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/InsertTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/InsertTest.kt deleted file mode 100644 index 21178f025..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/InsertTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import android.content.ContentValues -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.insert -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.set -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.TwoColumnModel -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.name -import org.junit.Assert.assertEquals -import org.junit.Test - -class InsertTest : BaseUnitTest() { - - @Test - fun validateInsert() { - assertEquals("INSERT INTO `SimpleModel` VALUES('something')", insert().values("something").query.trim()) - } - - @Test - fun validateInsertOr() { - assertEquals("INSERT OR REPLACE INTO `SimpleModel` VALUES('something')", insert().orReplace().values("something").query.trim()) - assertEquals("INSERT OR FAIL INTO `SimpleModel` VALUES('something')", insert().orFail().values("something").query.trim()) - assertEquals("INSERT OR IGNORE INTO `SimpleModel` VALUES('something')", insert().orIgnore().values("something").query.trim()) - assertEquals("INSERT OR REPLACE INTO `SimpleModel` VALUES('something')", insert().orReplace().values("something").query.trim()) - assertEquals("INSERT OR ROLLBACK INTO `SimpleModel` VALUES('something')", insert().orRollback().values("something").query.trim()) - assertEquals("INSERT OR ABORT INTO `SimpleModel` VALUES('something')", insert().orAbort().values("something").query.trim()) - } - - @Test - fun validateInsertProjection() { - assertEquals("INSERT INTO `TwoColumnModel`(`name`, `id`) VALUES('name', 'id')", - insert().columns(name, id).values("name", "id").query.trim()) - } - - @Test - fun validateSelect() { - assertEquals("INSERT INTO `TwoColumnModel` SELECT * FROM `SimpleModel`", - insert().select(select from SimpleModel::class).query.trim()) - } - - @Test - fun validateColumns() { - assertEquals("INSERT INTO `TwoColumnModel`(`name`, `id`) VALUES('name', 'id')", - insert().asColumns().values("name", "id").query.trim()) - assertEquals("INSERT INTO `TwoColumnModel`(`name`, `id`) VALUES('name', 'id')", - insert().columns("name", "id").values("name", "id").query.trim()) - assertEquals("INSERT INTO `TwoColumnModel`(`name`, `id`) VALUES('name', 'id')", - insert().columns(listOf(name, id)).values("name", "id").query.trim()) - } - - @Test - fun validateColumnValues() { - assertEquals("INSERT INTO `TwoColumnModel`(`name`, `id`) VALUES('name', 0)", - insert().columnValues(name.eq("name"), id.eq(0)).query.trim()) - assertEquals("INSERT INTO `TwoColumnModel`(`name`, `id`) VALUES('name', 0)", - insert().columnValues(Operator.op(NameAlias.builder("name").build()).eq("name"), - id.eq(0)).query.trim()) - assertEquals("INSERT INTO `TwoColumnModel`(`name`, `id`) VALUES('name', 0)", - insert().columnValues(OperatorGroup.clause().andAll(name.eq("name"), id.eq(0))).query.trim()) - - val contentValues = ContentValues() - contentValues["name"] = "name" - contentValues["id"] = 0.toInt() - - assertEquals("INSERT INTO `TwoColumnModel`(`name`, `id`) VALUES('name', 0)", - insert().columnValues(contentValues).query.trim()) - - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/JoinTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/JoinTest.kt deleted file mode 100644 index be2acea20..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/JoinTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.crossJoin -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.innerJoin -import com.raizlabs.android.dbflow.kotlinextensions.leftOuterJoin -import com.raizlabs.android.dbflow.kotlinextensions.naturalJoin -import com.raizlabs.android.dbflow.kotlinextensions.on -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.using -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import com.raizlabs.android.dbflow.models.TwoColumnModel -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table -import org.junit.Assert.assertEquals -import org.junit.Test - - -class JoinTest : BaseUnitTest() { - - @Test - fun validateAliasJoin() { - assertEquals("SELECT * FROM `SimpleModel` INNER JOIN `TwoColumnModel` AS `Name` ON `TwoColumnModel`.`name`=`name`", - ((select from SimpleModel::class innerJoin - TwoColumnModel::class).`as`("Name") on TwoColumnModel_Table.name.withTable().eq(SimpleModel_Table.name)).query.trim()) - } - - @Test - fun testInnerJoin() { - val join = select from SimpleModel::class innerJoin - TwoColumnModel::class on TwoColumnModel_Table.name.withTable().eq(SimpleModel_Table.name) - assertEquals("SELECT * FROM `SimpleModel` INNER JOIN `TwoColumnModel` ON `TwoColumnModel`.`name`=`name`", - join.query.trim()) - } - - @Test - fun testLeftOuterJoin() { - val join = select from SimpleModel::class leftOuterJoin - TwoColumnModel::class on TwoColumnModel_Table.name.withTable().eq(SimpleModel_Table.name) - assertEquals("SELECT * FROM `SimpleModel` LEFT OUTER JOIN `TwoColumnModel` ON `TwoColumnModel`.`name`=`name`", - join.query.trim()) - } - - @Test - fun testCrossJoin() { - val join = select from SimpleModel::class crossJoin - TwoColumnModel::class on TwoColumnModel_Table.name.withTable().eq(SimpleModel_Table.name) - assertEquals("SELECT * FROM `SimpleModel` CROSS JOIN `TwoColumnModel` ON `TwoColumnModel`.`name`=`name`", - join.query.trim()) - } - - @Test - fun testMultiJoin() { - val join = select from SimpleModel::class innerJoin - TwoColumnModel::class on TwoColumnModel_Table.name.withTable().eq(SimpleModel_Table.name) crossJoin - TwoColumnModel::class on TwoColumnModel_Table.id.withTable().eq(SimpleModel_Table.name) - assertEquals("SELECT * FROM `SimpleModel` INNER JOIN `TwoColumnModel` ON `TwoColumnModel`.`name`=`name`" + - " CROSS JOIN `TwoColumnModel` ON `TwoColumnModel`.`id`=`name`", - join.query.trim()) - } - - @Test - fun testInnerJoinOnUsing() { - val join = select from SimpleModel::class innerJoin - TwoColumnModel::class using SimpleModel_Table.name.withTable() - assertEquals("SELECT * FROM `SimpleModel` INNER JOIN `TwoColumnModel` USING (`SimpleModel`.`name`)", - join.query.trim()) - } - - @Test - fun testNaturalJoin() { - val join = (select from SimpleModel::class naturalJoin - TwoColumnModel::class).end() - assertEquals("SELECT * FROM `SimpleModel` NATURAL JOIN `TwoColumnModel`", - join.query.trim()) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/NameAliasTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/NameAliasTest.kt deleted file mode 100644 index 8b9854071..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/NameAliasTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.`as` -import com.raizlabs.android.dbflow.kotlinextensions.nameAlias -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Test - -class NameAliasTest : BaseUnitTest() { - - @Test - fun testSimpleCase() { - assertEquals("`name`", "name".nameAlias.query) - } - - @Test - fun testAlias() { - assertEquals("`name` AS `alias`", "name".`as`("alias").fullQuery) - } - - @Test - fun validateBuilder() { - val nameAlias = NameAlias.builder("name") - .keyword("DISTINCT") - .`as`("Alias") - .withTable("MyTable") - .shouldAddIdentifierToAliasName(false) - .shouldAddIdentifierToName(false) - .shouldStripAliasName(false) - .shouldStripIdentifier(false).build() - assertEquals("DISTINCT", nameAlias.keyword()) - assertEquals("Alias", nameAlias.aliasName()) - assertEquals("Alias", nameAlias.aliasNameRaw()) - assertEquals("`MyTable`", nameAlias.tableName()) - assertFalse(nameAlias.shouldStripAliasName()) - assertFalse(nameAlias.shouldStripIdentifier()) - assertEquals("Alias", nameAlias.nameAsKey) - assertEquals("DISTINCT `MyTable`.name AS Alias", nameAlias.fullQuery) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OperatorGroupTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OperatorGroupTest.kt deleted file mode 100644 index 128a0d018..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OperatorGroupTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.kotlinextensions.and -import com.raizlabs.android.dbflow.kotlinextensions.andAll -import com.raizlabs.android.dbflow.kotlinextensions.or -import com.raizlabs.android.dbflow.kotlinextensions.orAll -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.name -import org.junit.Test - -class OperatorGroupTest : BaseUnitTest() { - - - @Test - fun validateCommaSeparated() { - assertEquals("(`name`='name', `id`=0)", OperatorGroup.clause().setAllCommaSeparated(true).andAll(name.eq("name"), id.eq(0))) - } - - @Test - fun validateParanthesis() { - assertEquals("`name`='name'", OperatorGroup.nonGroupingClause(name.eq("name")).setUseParenthesis(false)) - } - - @Test - fun validateOr() { - assertEquals("(`name`='name' OR `id`=0)", name.eq("name") or id.eq(0)) - } - - @Test - fun validateOrAll() { - assertEquals("(`name`='name' OR `id`=0 OR `name`='test')", name.eq("name") orAll arrayListOf(id.eq(0), name.eq("test"))) - } - - @Test - fun validateAnd() { - assertEquals("(`name`='name' AND `id`=0)", name.eq("name") and id.eq(0)) - } - - @Test - fun validateAndAll() { - assertEquals("(`name`='name' AND `id`=0 AND `name`='test')", name.eq("name") andAll arrayListOf(id.eq(0), name.eq("test"))) - } - -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OperatorTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OperatorTest.kt deleted file mode 100644 index b557c1d1f..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OperatorTest.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.annotation.Collate -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.kotlinextensions.and -import com.raizlabs.android.dbflow.kotlinextensions.between -import com.raizlabs.android.dbflow.kotlinextensions.collate -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.op -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import org.junit.Test - -class OperatorTest : BaseUnitTest() { - - @Test - fun testEquals() { - assertEquals("`name`='name'", "name".op().eq("name")) - assertEquals("`name`='name'", "name".op().`is`("name")) - } - - @Test - fun testNotEquals() { - assertEquals("`name`!='name'", "name".op().notEq("name")) - assertEquals("`name`!='name'", "name".op().isNot("name")) - } - - @Test - fun testLike() { - assertEquals("`name` LIKE 'name'", "name".op().like("name")) - assertEquals("`name` NOT LIKE 'name'", "name".op().notLike("name")) - assertEquals("`name` GLOB 'name'", "name".op().glob("name")) - } - - @Test - fun testMath() { - assertEquals("`name`>'name'", "name".op().greaterThan("name")) - assertEquals("`name`>='name'", "name".op().greaterThanOrEq("name")) - assertEquals("`name`<'name'", "name".op().lessThan("name")) - assertEquals("`name`<='name'", "name".op().lessThanOrEq("name")) - assertEquals("`name`+'name'", "name".op() + "name") - assertEquals("`name`-'name'", "name".op() - "name") - assertEquals("`name`/'name'", "name".op() / "name") - assertEquals("`name`*'name'", "name".op() * "name") - assertEquals("`name`%'name'", "name".op() % "name") - } - - @Test - fun testCollate() { - assertEquals("`name` COLLATE NOCASE", "name".op() collate Collate.NOCASE) - assertEquals("`name` COLLATE NOCASE", "name".op() collate "NOCASE") - } - - @Test - fun testBetween() { - assertEquals("`id` BETWEEN 6 AND 7", id between 6 and 7) - } - - @Test - fun testIn() { - assertEquals("`id` IN (5,6,7,8,9)", id.`in`(5, 6, 7, 8) and 9) - assertEquals("`id` NOT IN (SELECT * FROM `SimpleModel`)", id.notIn(select from SimpleModel::class)) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OrderByTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OrderByTest.kt deleted file mode 100644 index 38c9b82f6..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/OrderByTest.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.annotation.Collate -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.kotlinextensions.collate -import com.raizlabs.android.dbflow.kotlinextensions.nameAlias -import com.raizlabs.android.dbflow.models.SimpleModel_Table.name -import org.junit.Test - -class OrderByTest : BaseUnitTest() { - - - @Test - fun validateBasicOrderBy() { - assertEquals("`name` ASC", OrderBy.fromProperty(name).ascending()) - } - - @Test - fun validateDescendingOrderBy() { - assertEquals("`name` DESC", OrderBy.fromNameAlias("name".nameAlias).descending()) - } - - @Test - fun validateCollate() { - assertEquals("`name` COLLATE RTRIM ASC", OrderBy.fromProperty(name).ascending() collate Collate.RTRIM) - } - - @Test - fun validateCustomOrdrBy() { - assertEquals("`name` ASC This is custom", OrderBy.fromString("`name` ASC This is custom")) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/SelectTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/SelectTest.kt deleted file mode 100644 index cd9f72d48..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/SelectTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.TwoColumnModel -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.name -import com.raizlabs.android.dbflow.sql.language.SQLite.select -import org.junit.Test - -class SelectTest : BaseUnitTest() { - - @Test - fun validateSelect() { - assertEquals("SELECT `name`,`id` FROM `TwoColumnModel`", select(name, id) from TwoColumnModel::class) - } - - @Test - fun validateSelectDistinct() { - assertEquals("SELECT DISTINCT `name` FROM `SimpleModel`", select(name).distinct() from SimpleModel::class) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/SetTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/SetTest.kt deleted file mode 100644 index f83a64dfc..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/SetTest.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table.name -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.sql.Query -import org.junit.Test - -class SetTest : BaseUnitTest() { - - @Test - fun validateSetWithConditions() { - assertEquals("SET `name`='name'", - Set(Query { "" }, SimpleModel::class.java).conditions(name.`is`("name"))) - } - - @Test - fun validateMultipleConditions() { - assertEquals("SET `name`='name', `id`=0", - Set(Query { "" }, SimpleModel::class.java) - .conditions(name.`is`("name"), id.`is`(0))) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/TriggerTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/TriggerTest.kt deleted file mode 100644 index 7a108451d..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/TriggerTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.kotlinextensions.and -import com.raizlabs.android.dbflow.kotlinextensions.begin -import com.raizlabs.android.dbflow.kotlinextensions.columnValues -import com.raizlabs.android.dbflow.kotlinextensions.createTrigger -import com.raizlabs.android.dbflow.kotlinextensions.eq -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.insert -import com.raizlabs.android.dbflow.kotlinextensions.insertOn -import com.raizlabs.android.dbflow.kotlinextensions.property -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.updateOn -import com.raizlabs.android.dbflow.kotlinextensions.where -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table.name -import com.raizlabs.android.dbflow.models.TwoColumnModel -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.sql.SQLiteType -import org.junit.Assert.assertNotNull -import org.junit.Test - -class TriggerTest : BaseUnitTest() { - - @Test - fun validateBasicTrigger() { - assertEquals("CREATE TRIGGER IF NOT EXISTS `MyTrigger` AFTER INSERT ON `SimpleModel` " + - "\nBEGIN" + - "\nINSERT INTO `TwoColumnModel`(`name`) VALUES(`new`.`name`);" + - "\nEND", - createTrigger("MyTrigger").after() insertOn SimpleModel::class begin - insert(TwoColumnModel::class).columnValues(name to NameAlias.ofTable("new", "name"))) - } - - @Test - fun validateUpdateTriggerMultiline() { - assertEquals("CREATE TEMP TRIGGER IF NOT EXISTS `MyTrigger` BEFORE UPDATE ON `SimpleModel` " + - "\nBEGIN" + - "\nINSERT INTO `TwoColumnModel`(`name`) VALUES(`new`.`name`);" + - "\nINSERT INTO `TwoColumnModel`(`id`) VALUES(CAST(`new`.`name` AS INTEGER));" + - "\nEND", - createTrigger("MyTrigger").temporary().before() updateOn SimpleModel::class begin - insert(TwoColumnModel::class).columnValues(name to NameAlias.ofTable("new", "name")) and - insert(TwoColumnModel::class) - .columnValues(id to Method.cast(NameAlias.ofTable("new", "name").property) - .`as`(SQLiteType.INTEGER))) - - } - - @Test - fun validateTriggerWorks() { - val trigger = createTrigger("MyTrigger").after() insertOn SimpleModel::class begin - insert(TwoColumnModel::class).columnValues(name to NameAlias.ofTable("new", "name")) - trigger.enable() - SimpleModel("Test").insert() - - val result = select from TwoColumnModel::class where (name eq "Test") - assertNotNull(result) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/UnsafeStringOperatorTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/UnsafeStringOperatorTest.kt deleted file mode 100644 index fd3998184..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/UnsafeStringOperatorTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.where -import com.raizlabs.android.dbflow.models.SimpleModel -import org.junit.Test - -class UnsafeStringOperatorTest : BaseUnitTest() { - - @Test - fun testCanIncludeInQuery() { - val op = UnSafeStringOperator("name = ?, id = ?, test = ?", arrayOf("'name'", "0", "'test'")) - assertEquals("name = 'name', id = 0, test = 'test'", op) - assertEquals("SELECT * FROM `SimpleModel` WHERE name = 'name', id = 0, test = 'test'", - select from SimpleModel::class where op) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/UpdateTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/UpdateTest.kt deleted file mode 100644 index fb7e0c46a..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/UpdateTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.annotation.ConflictAction -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.kotlinextensions.eq -import com.raizlabs.android.dbflow.kotlinextensions.set -import com.raizlabs.android.dbflow.kotlinextensions.update -import com.raizlabs.android.dbflow.models.NumberModel -import com.raizlabs.android.dbflow.models.NumberModel_Table.id -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table.name -import com.raizlabs.android.dbflow.sql.language.property.Property -import org.junit.Test - -class UpdateTest : BaseUnitTest() { - - @Test - fun validateUpdateRollback() { - assertEquals("UPDATE OR ROLLBACK `SimpleModel`", update().orRollback()) - } - - @Test - fun validateUpdateFail() { - assertEquals("UPDATE OR FAIL `SimpleModel`", update().orFail()) - } - - @Test - fun validateUpdateIgnore() { - assertEquals("UPDATE OR IGNORE `SimpleModel`", update().orIgnore()) - } - - @Test - fun validateUpdateReplace() { - assertEquals("UPDATE OR REPLACE `SimpleModel`", update().orReplace()) - } - - @Test - fun validateUpdateAbort() { - assertEquals("UPDATE OR ABORT `SimpleModel`", update().orAbort()) - } - - @Test - fun validateSetQuery() { - assertEquals("UPDATE `SimpleModel` SET `name`='name'", update() set (name eq "name")) - } - - @Test - fun validateWildcardQuery() { - assertEquals("UPDATE OR FAIL `NumberModel` SET `id`=? WHERE `id`=?", - update().or(ConflictAction.FAIL) - .set(id.eq(Property.WILDCARD)) - .where(id.eq(Property.WILDCARD))) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/WhereTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/WhereTest.kt deleted file mode 100644 index d6b053ebd..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/WhereTest.kt +++ /dev/null @@ -1,138 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.assertEquals -import com.raizlabs.android.dbflow.kotlinextensions.and -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.groupBy -import com.raizlabs.android.dbflow.kotlinextensions.having -import com.raizlabs.android.dbflow.kotlinextensions.limit -import com.raizlabs.android.dbflow.kotlinextensions.list -import com.raizlabs.android.dbflow.kotlinextensions.nameAlias -import com.raizlabs.android.dbflow.kotlinextensions.offset -import com.raizlabs.android.dbflow.kotlinextensions.or -import com.raizlabs.android.dbflow.kotlinextensions.orderBy -import com.raizlabs.android.dbflow.kotlinextensions.result -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.kotlinextensions.update -import com.raizlabs.android.dbflow.kotlinextensions.where -import com.raizlabs.android.dbflow.kotlinextensions.whereExists -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table.name -import com.raizlabs.android.dbflow.models.TwoColumnModel -import com.raizlabs.android.dbflow.models.TwoColumnModel_Table.id -import com.raizlabs.android.dbflow.sql.language.OrderBy.fromNameAlias -import com.raizlabs.android.dbflow.sql.language.SQLite.select -import org.junit.Assert.fail -import org.junit.Test - -class WhereTest : BaseUnitTest() { - - @Test - fun validateBasicWhere() { - assertEquals("SELECT * FROM `SimpleModel` WHERE `name`='name'", - select from SimpleModel::class where name.`is`("name")) - } - - @Test - fun validateComplexQueryWhere() { - assertEquals("SELECT * FROM `SimpleModel` WHERE `name`='name' OR `id`=1 AND (`id`=0 OR `name`='hi')", - select from SimpleModel::class where name.`is`("name") or id.eq(1) and (id.`is`(0) or name.eq("hi"))) - } - - @Test - fun validateGroupBy() { - assertEquals("SELECT * FROM `SimpleModel` WHERE `name`='name' GROUP BY `name`", - select from SimpleModel::class where name.`is`("name") groupBy name) - } - - @Test - fun validateGroupByNameAlias() { - assertEquals("SELECT * FROM `SimpleModel` WHERE `name`='name' GROUP BY `name`,`id`", - (select from SimpleModel::class where name.`is`("name")).groupBy("name".nameAlias, "id".nameAlias)) - } - - @Test - fun validateGroupByNameProps() { - assertEquals("SELECT * FROM `SimpleModel` WHERE `name`='name' GROUP BY `name`,`id`", - (select from SimpleModel::class where name.`is`("name")).groupBy(name, id)) - } - - @Test - fun validateHaving() { - assertEquals("SELECT * FROM `SimpleModel` WHERE `name`='name' HAVING `name` LIKE 'That'", - select from SimpleModel::class where name.`is`("name") having name.like("That")) - } - - @Test - fun validateLimit() { - assertEquals("SELECT * FROM `SimpleModel` WHERE `name`='name' LIMIT 10", - select from SimpleModel::class where name.`is`("name") limit 10) - } - - @Test - fun validateOffset() { - assertEquals("SELECT * FROM `SimpleModel` WHERE `name`='name' OFFSET 10", - select from SimpleModel::class where name.`is`("name") offset 10) - } - - @Test - fun validateWhereExists() { - assertEquals("SELECT * FROM `SimpleModel` " + - "WHERE EXISTS (SELECT `name` FROM `SimpleModel` WHERE `name` LIKE 'Andrew')", - select from SimpleModel::class - whereExists (select(name) from SimpleModel::class where name.like("Andrew"))) - } - - @Test - fun validateOrderByWhere() { - assertEquals("SELECT * FROM `SimpleModel` " + - "WHERE `name`='name' ORDER BY `name` ASC", - (select from SimpleModel::class - where name.eq("name")).orderBy(name, true)) - } - - @Test - fun validateOrderByWhereAlias() { - assertEquals("SELECT * FROM `SimpleModel` " + - "WHERE `name`='name' ORDER BY `name` ASC", - (select from SimpleModel::class - where name.eq("name")).orderBy("name".nameAlias, true)) - } - - @Test - fun validateOrderBy() { - assertEquals("SELECT * FROM `SimpleModel` " + - "WHERE `name`='name' ORDER BY `name` ASC", - select from SimpleModel::class - where name.eq("name") orderBy fromNameAlias("name".nameAlias).ascending()) - } - - @Test - fun validateOrderByAll() { - assertEquals("SELECT * FROM `TwoColumnModel` " + - "WHERE `name`='name' ORDER BY `name` ASC,`id` DESC", - (select from TwoColumnModel::class - where name.eq("name")) - .orderByAll(listOf( - fromNameAlias("name".nameAlias).ascending(), - fromNameAlias("id".nameAlias).descending()))) - } - - @Test - fun validateNonSelectThrowError() { - try { - update().set(name.`is`("name")).result - fail("Non select passed") - } catch (i: IllegalArgumentException) { - // expected - } - - try { - update().set(name.`is`("name")).list - fail("Non select passed") - } catch (i: IllegalArgumentException) { - // expected - } - } -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/property/IndexPropertyTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/property/IndexPropertyTest.kt deleted file mode 100644 index 5c0cb127f..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/language/property/IndexPropertyTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language.property - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import org.junit.Assert.assertEquals -import org.junit.Test - -class IndexPropertyTest : BaseUnitTest() { - - - @Test - fun validateIndexProperty() { - - val prop = IndexProperty("Index", true, SimpleModel::class.java, - SimpleModel_Table.name) - prop.createIfNotExists() - prop.drop() - assertEquals("`Index`", prop.indexName) - } -} \ No newline at end of file diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigrationTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigrationTest.kt deleted file mode 100644 index 767d35a84..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigrationTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.raizlabs.android.dbflow.sql.migration - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.writableDatabaseForTable -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.models.SimpleModel_Table -import org.junit.Test - -/** - * Description: - */ - -class UpdateTableMigrationTest : BaseUnitTest() { - - - @Test - fun testUpdateMigrationQuery() { - val update = UpdateTableMigration(SimpleModel::class.java) - update.set(SimpleModel_Table.name.eq("yes")) - update.migrate(writableDatabaseForTable()) - } -} diff --git a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/queriable/AsyncQueryTest.kt b/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/queriable/AsyncQueryTest.kt deleted file mode 100644 index 10925cc4f..000000000 --- a/dbflow-tests/src/test/java/com/raizlabs/android/dbflow/sql/queriable/AsyncQueryTest.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable - -import com.raizlabs.android.dbflow.BaseUnitTest -import com.raizlabs.android.dbflow.kotlinextensions.async -import com.raizlabs.android.dbflow.kotlinextensions.cursorResult -import com.raizlabs.android.dbflow.kotlinextensions.from -import com.raizlabs.android.dbflow.kotlinextensions.list -import com.raizlabs.android.dbflow.kotlinextensions.result -import com.raizlabs.android.dbflow.kotlinextensions.save -import com.raizlabs.android.dbflow.kotlinextensions.select -import com.raizlabs.android.dbflow.models.SimpleModel -import com.raizlabs.android.dbflow.sql.language.CursorResult -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Test - -class AsyncQueryTest : BaseUnitTest() { - - @Test - fun testQueryResult() { - SimpleModel("name").save() - - var model: SimpleModel? = null - (select from SimpleModel::class).async result { _, result -> - model = result - } - assertNotNull(model) - assertEquals("name", model?.name) - } - - @Test - fun testQueryListResult() { - SimpleModel("name").save() - SimpleModel("name2").save() - - var list = mutableListOf() - (select from SimpleModel::class).async list { _, mutableList -> - list = mutableList - } - assertEquals(2, list.size) - } - - @Test - fun testQueryListCursorResult() { - SimpleModel("name").save() - SimpleModel("name2").save() - - var result: CursorResult? = null - (select from SimpleModel::class).async cursorResult { _, cursorResult -> - result = cursorResult - } - assertNotNull(result) - assertEquals(2L, result?.count) - result?.close() - } -} \ No newline at end of file diff --git a/dbflow/build.gradle b/dbflow/build.gradle deleted file mode 100644 index 896bdc472..000000000 --- a/dbflow/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply plugin: 'com.android.library' - -project.ext.artifactId = bt_name - -android { - compileSdkVersion Integer.valueOf(dbflow_target_sdk) - buildToolsVersion dbflow_build_tools_version - - defaultConfig { - minSdkVersion dbflow_min_sdk - targetSdkVersion Integer.valueOf(dbflow_target_sdk) - versionCode = version_code - } - - lintOptions { - abortOnError false - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - -} - -dependencies { - api project("${dbflow_project_prefix}dbflow-core") - api "com.android.support:support-annotations:26.0.1" -} - -apply from: '../android-artifacts.gradle' diff --git a/dbflow/src/main/AndroidManifest.xml b/dbflow/src/main/AndroidManifest.xml deleted file mode 100644 index 166b778f3..000000000 --- a/dbflow/src/main/AndroidManifest.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseConfig.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseConfig.java deleted file mode 100644 index 11c3d50cd..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseConfig.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.raizlabs.android.dbflow.config; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.StringUtils; -import com.raizlabs.android.dbflow.runtime.BaseTransactionManager; -import com.raizlabs.android.dbflow.runtime.ModelNotifier; -import com.raizlabs.android.dbflow.structure.database.DatabaseHelperListener; -import com.raizlabs.android.dbflow.structure.database.OpenHelper; - -import java.util.HashMap; -import java.util.Map; - -/** - * Description: - */ -public final class DatabaseConfig { - - public static DatabaseConfig.Builder builder(@NonNull Class database) { - return new DatabaseConfig.Builder(database); - } - - public static DatabaseConfig.Builder inMemoryBuilder(@NonNull Class database) { - return new DatabaseConfig.Builder(database).inMemory(); - } - - public interface OpenHelperCreator { - - OpenHelper createHelper(DatabaseDefinition databaseDefinition, DatabaseHelperListener helperListener); - } - - public interface TransactionManagerCreator { - - BaseTransactionManager createManager(DatabaseDefinition databaseDefinition); - } - - private final OpenHelperCreator openHelperCreator; - private final Class databaseClass; - private final TransactionManagerCreator transactionManagerCreator; - private final DatabaseHelperListener helperListener; - private final Map, TableConfig> tableConfigMap; - private final ModelNotifier modelNotifier; - private final boolean inMemory; - private final String databaseName; - private final String databaseExtensionName; - - DatabaseConfig(Builder builder) { - openHelperCreator = builder.openHelperCreator; - databaseClass = builder.databaseClass; - transactionManagerCreator = builder.transactionManagerCreator; - helperListener = builder.helperListener; - tableConfigMap = builder.tableConfigMap; - modelNotifier = builder.modelNotifier; - inMemory = builder.inMemory; - if (builder.databaseName == null) { - databaseName = builder.databaseClass.getSimpleName(); - } else { - databaseName = builder.databaseName; - } - - if (builder.databaseExtensionName == null) { - databaseExtensionName = ".db"; - } else { - databaseExtensionName = StringUtils.isNotNullOrEmpty(builder.databaseExtensionName) - ? "." + builder.databaseExtensionName : ""; - } - } - - @NonNull - public String getDatabaseExtensionName() { - return databaseExtensionName; - } - - public boolean isInMemory() { - return inMemory; - } - - @NonNull - public String getDatabaseName() { - return databaseName; - } - - @Nullable - public OpenHelperCreator helperCreator() { - return openHelperCreator; - } - - @Nullable - public DatabaseHelperListener helperListener() { - return helperListener; - } - - @NonNull - public Class databaseClass() { - return databaseClass; - } - - @Nullable - public TransactionManagerCreator transactionManagerCreator() { - return transactionManagerCreator; - } - - @Nullable - public ModelNotifier modelNotifier() { - return modelNotifier; - } - - @NonNull - public Map, TableConfig> tableConfigMap() { - return tableConfigMap; - } - - @SuppressWarnings("unchecked") - @Nullable - public TableConfig getTableConfigForTable(Class modelClass) { - return tableConfigMap().get(modelClass); - } - - public static final class Builder { - - OpenHelperCreator openHelperCreator; - final Class databaseClass; - TransactionManagerCreator transactionManagerCreator; - DatabaseHelperListener helperListener; - final Map, TableConfig> tableConfigMap = new HashMap<>(); - ModelNotifier modelNotifier; - boolean inMemory = false; - String databaseName; - String databaseExtensionName; - - public Builder(@NonNull Class databaseClass) { - this.databaseClass = databaseClass; - } - - public Builder transactionManagerCreator(TransactionManagerCreator transactionManager) { - this.transactionManagerCreator = transactionManager; - return this; - } - - public Builder helperListener(DatabaseHelperListener helperListener) { - this.helperListener = helperListener; - return this; - } - - public Builder addTableConfig(TableConfig tableConfig) { - tableConfigMap.put(tableConfig.tableClass(), tableConfig); - return this; - } - - public Builder modelNotifier(ModelNotifier modelNotifier) { - this.modelNotifier = modelNotifier; - return this; - } - - @NonNull - public Builder inMemory() { - inMemory = true; - return this; - } - - /** - * @return Pass in dynamic database name here. Otherwise it defaults to class name. - */ - @NonNull - public Builder databaseName(String name) { - databaseName = name; - return this; - } - - /** - * @return Pass in the extension for the DB here. - * Otherwise defaults to ".db". If empty string passed, no extension is used. - */ - public Builder extensionName(String name) { - databaseExtensionName = name; - return this; - } - - /** - * Overrides the default {@link OpenHelper} for a {@link DatabaseDefinition}. - * - * @param openHelper The openhelper to use. - */ - public Builder openHelper(OpenHelperCreator openHelper) { - openHelperCreator = openHelper; - return this; - } - - public DatabaseConfig build() { - return new DatabaseConfig(this); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java deleted file mode 100644 index 637246f61..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseDefinition.java +++ /dev/null @@ -1,444 +0,0 @@ -package com.raizlabs.android.dbflow.config; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.Database; -import com.raizlabs.android.dbflow.annotation.QueryModel; -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.runtime.BaseTransactionManager; -import com.raizlabs.android.dbflow.runtime.ContentResolverNotifier; -import com.raizlabs.android.dbflow.runtime.ModelNotifier; -import com.raizlabs.android.dbflow.sql.migration.Migration; -import com.raizlabs.android.dbflow.structure.BaseModelView; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.ModelViewAdapter; -import com.raizlabs.android.dbflow.structure.QueryModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseHelperListener; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowSQLiteOpenHelper; -import com.raizlabs.android.dbflow.structure.database.OpenHelper; -import com.raizlabs.android.dbflow.structure.database.transaction.DefaultTransactionManager; -import com.raizlabs.android.dbflow.structure.database.transaction.DefaultTransactionQueue; -import com.raizlabs.android.dbflow.structure.database.transaction.ITransaction; -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * Description: The main interface that all Database implementations extend from. This is for internal usage only - * as it will be generated for every {@link Database}. - */ -@SuppressWarnings("NullableProblems") -public abstract class DatabaseDefinition { - - private final Map> migrationMap = new HashMap<>(); - - private final Map, ModelAdapter> modelAdapters = new HashMap<>(); - - private final Map> modelTableNames = new HashMap<>(); - - private final Map, ModelViewAdapter> modelViewAdapterMap = new LinkedHashMap<>(); - - private final Map, QueryModelAdapter> queryModelAdapterMap = new LinkedHashMap<>(); - - /** - * The helper that manages database changes and initialization - */ - private OpenHelper openHelper; - - /** - * Allows for the app to listen for database changes. - */ - private DatabaseHelperListener helperListener; - - /** - * Used when resetting the DB - */ - private boolean isResetting = false; - - @NonNull - private BaseTransactionManager transactionManager; - - @Nullable - private DatabaseConfig databaseConfig; - - @Nullable - private ModelNotifier modelNotifier; - - public DatabaseDefinition() { - applyDatabaseConfig(FlowManager.getConfig().databaseConfigMap().get(getAssociatedDatabaseClassFile())); - } - - /** - * Applies a database configuration object to this class. - */ - @SuppressWarnings({"unchecked", "ConstantConditions"}) - void applyDatabaseConfig(@Nullable DatabaseConfig databaseConfig) { - this.databaseConfig = databaseConfig; - if (databaseConfig != null) { - // initialize configuration if exists. - Collection tableConfigCollection = databaseConfig.tableConfigMap().values(); - for (TableConfig tableConfig : tableConfigCollection) { - ModelAdapter modelAdapter = modelAdapters.get(tableConfig.tableClass()); - if (modelAdapter == null) { - continue; - } - if (tableConfig.listModelLoader() != null) { - modelAdapter.setListModelLoader(tableConfig.listModelLoader()); - } - - if (tableConfig.singleModelLoader() != null) { - modelAdapter.setSingleModelLoader(tableConfig.singleModelLoader()); - } - - if (tableConfig.modelSaver() != null) { - modelAdapter.setModelSaver(tableConfig.modelSaver()); - } - - } - helperListener = databaseConfig.helperListener(); - } - if (databaseConfig == null || databaseConfig.transactionManagerCreator() == null) { - transactionManager = new DefaultTransactionManager(this); - } else { - transactionManager = databaseConfig.transactionManagerCreator().createManager(this); - } - } - - protected void addModelAdapter(ModelAdapter modelAdapter, DatabaseHolder holder) { - holder.putDatabaseForTable(modelAdapter.getModelClass(), this); - modelTableNames.put(modelAdapter.getTableName(), modelAdapter.getModelClass()); - modelAdapters.put(modelAdapter.getModelClass(), modelAdapter); - } - - protected void addModelViewAdapter(ModelViewAdapter modelViewAdapter, DatabaseHolder holder) { - holder.putDatabaseForTable(modelViewAdapter.getModelClass(), this); - modelViewAdapterMap.put(modelViewAdapter.getModelClass(), modelViewAdapter); - } - - protected void addQueryModelAdapter(QueryModelAdapter queryModelAdapter, DatabaseHolder holder) { - holder.putDatabaseForTable(queryModelAdapter.getModelClass(), this); - queryModelAdapterMap.put(queryModelAdapter.getModelClass(), queryModelAdapter); - } - - protected void addMigration(int version, Migration migration) { - List list = migrationMap.get(version); - if (list == null) { - list = new ArrayList<>(); - migrationMap.put(version, list); - } - list.add(migration); - } - - /** - * @return a list of all model classes in this database. - */ - @NonNull - public List> getModelClasses() { - return new ArrayList<>(modelAdapters.keySet()); - } - - @NonNull - public BaseTransactionManager getTransactionManager() { - return transactionManager; - } - - /** - * Internal method used to create the database schema. - * - * @return List of Model Adapters - */ - @NonNull - public List getModelAdapters() { - return new ArrayList<>(modelAdapters.values()); - } - - /** - * Returns the associated {@link ModelAdapter} within this database for - * the specified table. If the Model is missing the {@link Table} annotation, - * this will return null. - * - * @param table The model that exists in this database. - * @return The ModelAdapter for the table. - */ - @SuppressWarnings("unchecked") - @Nullable - public ModelAdapter getModelAdapterForTable(Class table) { - return modelAdapters.get(table); - } - - /** - * @param tableName The name of the table in this db. - * @return The associated {@link ModelAdapter} within this database for the specified table name. - * If the Model is missing the {@link Table} annotation, this will return null. - */ - @Nullable - public Class getModelClassForName(String tableName) { - return modelTableNames.get(tableName); - } - - /** - * @return the {@link BaseModelView} list for this database. - */ - @NonNull - public List> getModelViews() { - return new ArrayList<>(modelViewAdapterMap.keySet()); - } - - /** - * @param table the VIEW class to retrieve the ModelViewAdapter from. - * @return the associated {@link ModelViewAdapter} for the specified table. - */ - @SuppressWarnings("unchecked") - @Nullable - public ModelViewAdapter getModelViewAdapterForTable(Class table) { - return modelViewAdapterMap.get(table); - } - - /** - * @return The list of {@link ModelViewAdapter}. Internal method for - * creating model views in the DB. - */ - @NonNull - public List getModelViewAdapters() { - return new ArrayList<>(modelViewAdapterMap.values()); - } - - /** - * @return The list of {@link QueryModelAdapter}. Internal method for creating query models in the DB. - */ - @NonNull - public List getModelQueryAdapters() { - return new ArrayList<>(queryModelAdapterMap.values()); - } - - /** - * @param queryModel The {@link QueryModel} class - * @return The adapter that corresponds to the specified class. - */ - @SuppressWarnings("unchecked") - @Nullable - public QueryModelAdapter getQueryModelAdapterForQueryClass(Class queryModel) { - return queryModelAdapterMap.get(queryModel); - } - - /** - * @return The map of migrations to DB version - */ - @NonNull - public Map> getMigrations() { - return migrationMap; - } - - @NonNull - public synchronized OpenHelper getHelper() { - if (openHelper == null) { - DatabaseConfig config = FlowManager.getConfig().databaseConfigMap() - .get(getAssociatedDatabaseClassFile()); - if (config == null || config.helperCreator() == null) { - openHelper = new FlowSQLiteOpenHelper(this, helperListener); - } else { - openHelper = config.helperCreator().createHelper(this, helperListener); - } - openHelper.performRestoreFromBackup(); - } - return openHelper; - } - - @NonNull - public DatabaseWrapper getWritableDatabase() { - return getHelper().getDatabase(); - } - - @NonNull - public ModelNotifier getModelNotifier() { - if (modelNotifier == null) { - DatabaseConfig config = FlowManager.getConfig().databaseConfigMap() - .get(getAssociatedDatabaseClassFile()); - if (config == null || config.modelNotifier() == null) { - modelNotifier = new ContentResolverNotifier(FlowManager.DEFAULT_AUTHORITY); - } else { - modelNotifier = config.modelNotifier(); - } - } - return modelNotifier; - } - - @NonNull - public Transaction.Builder beginTransactionAsync(@NonNull ITransaction transaction) { - return new Transaction.Builder(transaction, this); - } - - public void executeTransaction(@NonNull ITransaction transaction) { - DatabaseWrapper database = getWritableDatabase(); - try { - database.beginTransaction(); - transaction.execute(database); - database.setTransactionSuccessful(); - } finally { - database.endTransaction(); - } - } - - /** - * @return The name of this database as defined in {@link Database} - */ - @NonNull - public String getDatabaseName() { - return databaseConfig != null ? databaseConfig.getDatabaseName() : getAssociatedDatabaseClassFile().getSimpleName(); - } - - /** - * @return The file name that this database points to - */ - @NonNull - public String getDatabaseFileName() { - return getDatabaseName() + getDatabaseExtensionName(); - } - - /** - * @return the extension for the file name. - */ - @NonNull - public String getDatabaseExtensionName() { - return databaseConfig != null ? databaseConfig.getDatabaseExtensionName() : ".db"; - } - - /** - * @return True if the database will reside in memory. - */ - public boolean isInMemory() { - return databaseConfig != null && databaseConfig.isInMemory(); - } - - /** - * @return The version of the database currently. - */ - public abstract int getDatabaseVersion(); - - /** - * @return True if the {@link Database#consistencyCheckEnabled()} annotation is true. - */ - public abstract boolean areConsistencyChecksEnabled(); - - /** - * @return True if the {@link Database#foreignKeyConstraintsEnforced()} annotation is true. - */ - public abstract boolean isForeignKeysSupported(); - - /** - * @return True if the {@link Database#backupEnabled()} annotation is true. - */ - public abstract boolean backupEnabled(); - - /** - * @return The class that defines the {@link Database} annotation. - */ - @NonNull - public abstract Class getAssociatedDatabaseClassFile(); - - - /** - * @deprecated use {@link #reset()} - */ - @Deprecated - public void reset(Context context) { - reset(databaseConfig); - } - - /** - * Performs a full deletion of this database. Reopens the {@link FlowSQLiteOpenHelper} as well. - * Reapplies the {@link DatabaseConfig} if we have one. - */ - public void reset() { - reset(databaseConfig); - } - - /** - * Performs a full deletion of this database. Reopens the {@link FlowSQLiteOpenHelper} as well. - * - * @param databaseConfig sets a new {@link DatabaseConfig} on this class. - */ - public void reset(@Nullable DatabaseConfig databaseConfig) { - if (!isResetting) { - destroy(); - // reapply configuration before opening it. - applyDatabaseConfig(databaseConfig); - getHelper().getDatabase(); - } - } - - /** - * Reopens the DB with the new {@link DatabaseConfig} specified. - */ - public void reopen(@Nullable DatabaseConfig databaseConfig) { - if (!isResetting) { - close(); - openHelper = null; - applyDatabaseConfig(databaseConfig); - getHelper().getDatabase(); - isResetting = false; - } - } - - /** - * Closes and reopens the database. - */ - public void reopen() { - reopen(databaseConfig); - } - - /** - * Deletes the underlying database and destroys it. - */ - public void destroy() { - if (!isResetting) { - isResetting = true; - close(); - FlowManager.getContext().deleteDatabase(getDatabaseFileName()); - openHelper = null; - isResetting = false; - } - } - - /** - * Closes the DB and stops the {@link BaseTransactionManager} - */ - public void close() { - getTransactionManager().stopQueue(); - for (ModelAdapter modelAdapter : modelAdapters.values()) { - modelAdapter.closeInsertStatement(); - modelAdapter.closeCompiledStatement(); - modelAdapter.closeDeleteStatement(); - modelAdapter.closeUpdateStatement(); - } - getHelper().closeDB(); - } - - /** - * @return True if the database is ok. If backups are enabled, we restore from backup and will - * override the return value if it replaces the main DB. - */ - public boolean isDatabaseIntegrityOk() { - return getHelper().isDatabaseIntegrityOk(); - } - - /** - * Saves the database as a backup on the {@link DefaultTransactionQueue}. This will - * create a THIRD database to use as a backup to the backup in case somehow the overwrite fails. - * - * @throws java.lang.IllegalStateException if {@link Database#backupEnabled()} - * or {@link Database#consistencyCheckEnabled()} is not enabled. - */ - public void backupDatabase() { - getHelper().backupDB(); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseHolder.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseHolder.java deleted file mode 100644 index 0900d38b6..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/DatabaseHolder.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.raizlabs.android.dbflow.config; - -import com.raizlabs.android.dbflow.converter.TypeConverter; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Description: The base interface for interacting with all of the database and top-level data that's shared - * between them. - */ -public abstract class DatabaseHolder { - protected final Map, DatabaseDefinition> databaseDefinitionMap = new HashMap<>(); - protected final Map databaseNameMap = new HashMap<>(); - protected final Map, DatabaseDefinition> databaseClassLookupMap = new HashMap<>(); - protected final Map, TypeConverter> typeConverters = new HashMap<>(); - - /** - * @param clazz The model value class to get a {@link com.raizlabs.android.dbflow.converter.TypeConverter} - * @return Type converter for the specified model value. - */ - public TypeConverter getTypeConverterForClass(Class clazz) { - return typeConverters.get(clazz); - } - - /** - * @param table The model class - * @return The database that the table belongs in - */ - public DatabaseDefinition getDatabaseForTable(Class table) { - return databaseDefinitionMap.get(table); - } - - public DatabaseDefinition getDatabase(Class databaseClass) { - return databaseClassLookupMap.get(databaseClass); - } - - /** - * @param databaseName The name of the database to retrieve - * @return The database that has the specified name - */ - public DatabaseDefinition getDatabase(String databaseName) { - return databaseNameMap.get(databaseName); - } - - /** - * Helper method used to store a database for the specified table. - * - * @param table The model table - * @param databaseDefinition The database definition - */ - public void putDatabaseForTable(Class table, DatabaseDefinition databaseDefinition) { - databaseDefinitionMap.put(table, databaseDefinition); - databaseNameMap.put(databaseDefinition.getDatabaseName(), databaseDefinition); - databaseClassLookupMap.put(databaseDefinition.getAssociatedDatabaseClassFile(), databaseDefinition); - } - - public void reset() { - databaseDefinitionMap.clear(); - databaseNameMap.clear(); - databaseClassLookupMap.clear(); - typeConverters.clear(); - } - - public List getDatabaseDefinitions() { - return new ArrayList<>(databaseNameMap.values()); - } - - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowConfig.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowConfig.java deleted file mode 100644 index 8a6136917..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowConfig.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.raizlabs.android.dbflow.config; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Description: The main configuration instance for DBFlow. This - */ -public final class FlowConfig { - - public static FlowConfig.Builder builder(Context context) { - return new FlowConfig.Builder(context); - } - - private final Set> databaseHolders; - private final Map, DatabaseConfig> databaseConfigMap; - private final Context context; - private final boolean openDatabasesOnInit; - - FlowConfig(Builder builder) { - databaseHolders = Collections.unmodifiableSet(builder.databaseHolders); - databaseConfigMap = builder.databaseConfigMap; - context = builder.context; - openDatabasesOnInit = builder.openDatabasesOnInit; - } - - @NonNull - public Set> databaseHolders() { - return databaseHolders; - } - - @NonNull - public Map, DatabaseConfig> databaseConfigMap() { - return databaseConfigMap; - } - - @Nullable - public DatabaseConfig getConfigForDatabase(@NonNull Class databaseClass) { - return databaseConfigMap().get(databaseClass); - } - - @NonNull - public Context getContext() { - return context; - } - - public boolean openDatabasesOnInit() { - return openDatabasesOnInit; - } - - public static class Builder { - - final Context context; - Set> databaseHolders = new HashSet<>(); - final Map, DatabaseConfig> databaseConfigMap = new HashMap<>(); - boolean openDatabasesOnInit; - - public Builder(Context context) { - this.context = context.getApplicationContext(); - } - - @NonNull - public Builder addDatabaseHolder(@NonNull Class databaseHolderClass) { - databaseHolders.add(databaseHolderClass); - return this; - } - - @NonNull - public Builder addDatabaseConfig(@NonNull DatabaseConfig databaseConfig) { - databaseConfigMap.put(databaseConfig.databaseClass(), databaseConfig); - return this; - } - - /** - * @param openDatabasesOnInit true if we want all databases open. - * @return True to open all associated databases in DBFlow on calling of {@link FlowManager#init(FlowConfig)} - */ - @NonNull - public Builder openDatabasesOnInit(boolean openDatabasesOnInit) { - this.openDatabasesOnInit = openDatabasesOnInit; - return this; - } - - @NonNull - public FlowConfig build() { - return new FlowConfig(this); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowLog.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowLog.java deleted file mode 100644 index f59c7f6d9..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowLog.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.raizlabs.android.dbflow.config; - -import android.os.Build; -import android.util.Log; - -/** - * Description: Mirrors {@link Log} with its own {@link Level} flag. - */ -public class FlowLog { - - public static final String TAG = FlowLog.class.getSimpleName(); - private static Level level = Level.E; - - /** - * Sets the minimum level that we wish to print out log statements with. - * The default is {@link Level#E}. - * - * @param level - */ - public static void setMinimumLoggingLevel(Level level) { - FlowLog.level = level; - } - - /** - * Logs information to the {@link Log} class. It wraps around the standard implementation. - * It uses the {@link #TAG} for messages and sends a null throwable. - * - * @param level The log level to use - * @param message The message to print out - */ - public static void log(Level level, String message) { - log(level, message, null); - } - - /** - * Logs information to the {@link Log} class. It wraps around the standard implementation. - * It uses the {@link #TAG} for messages - * - * @param level The log level to use - * @param message The message to print out - * @param throwable The optional stack trace to print - */ - public static void log(Level level, String message, Throwable throwable) { - log(level, TAG, message, throwable); - } - - /** - * Logs information to the {@link Log} class. It wraps around the standard implementation. - * - * @param level The log level to use - * @param tag The tag of the log - * @param message The message to print out - * @param throwable The optional stack trace to print - */ - public static void log(Level level, String tag, String message, Throwable throwable) { - if (isEnabled(level)) { - level.call(tag, message, throwable); - } - } - - /** - * Returns true if the logging level is lower than the specified {@link Level} - * - * @return - */ - public static boolean isEnabled(Level level) { - return level.ordinal() >= FlowLog.level.ordinal(); - } - - /** - * Logs a {@link java.lang.Throwable} as an error. - * - * @param throwable The stack trace to print - */ - public static void logError(Throwable throwable) { - log(Level.E, throwable); - } - - /** - * Logs information to the {@link Log} class. It wraps around the standard implementation. - * It uses the {@link #TAG} for messages and sends an empty message - * - * @param level The log level to use - * @param throwable The stack trace to print - */ - public static void log(Level level, Throwable throwable) { - log(level, TAG, "", throwable); - } - - /** - * Defines a log level that will execute - */ - public enum Level { - V { - @Override - void call(String tag, String message, Throwable throwable) { - Log.v(tag, message, throwable); - } - }, - D { - @Override - void call(String tag, String message, Throwable throwable) { - Log.d(tag, message, throwable); - } - }, - I { - @Override - void call(String tag, String message, Throwable throwable) { - Log.i(tag, message, throwable); - } - }, - W { - @Override - void call(String tag, String message, Throwable throwable) { - Log.w(tag, message, throwable); - } - }, - E { - @Override - void call(String tag, String message, Throwable throwable) { - Log.e(tag, message, throwable); - } - }, - WTF { - @Override - void call(String tag, String message, Throwable throwable) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { - Log.wtf(tag, message, throwable); - } else { - // If on older platform, we will just exaggerate the log message in the error level - Log.e(tag, "!!!!!!!!*******" + message + "********!!!!!!", throwable); - } - } - }; - - abstract void call(String tag, String message, Throwable throwable); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowManager.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowManager.java deleted file mode 100644 index 569c02173..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/FlowManager.java +++ /dev/null @@ -1,535 +0,0 @@ -package com.raizlabs.android.dbflow.config; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.converter.TypeConverter; -import com.raizlabs.android.dbflow.runtime.ModelNotifier; -import com.raizlabs.android.dbflow.runtime.TableNotifierRegister; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.migration.Migration; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.BaseModelView; -import com.raizlabs.android.dbflow.structure.BaseQueryModel; -import com.raizlabs.android.dbflow.structure.InstanceAdapter; -import com.raizlabs.android.dbflow.structure.InvalidDBConfiguration; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.ModelViewAdapter; -import com.raizlabs.android.dbflow.structure.QueryModelAdapter; -import com.raizlabs.android.dbflow.structure.RetrievalAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Description: The main entry point into the generated database code. It uses reflection to look up - * and construct the generated database holder class used in defining the structure for all databases - * used in this application. - */ -public class FlowManager { - - private static class GlobalDatabaseHolder extends DatabaseHolder { - - private boolean initialized = false; - - public void add(DatabaseHolder holder) { - databaseDefinitionMap.putAll(holder.databaseDefinitionMap); - databaseNameMap.putAll(holder.databaseNameMap); - typeConverters.putAll(holder.typeConverters); - databaseClassLookupMap.putAll(holder.databaseClassLookupMap); - initialized = true; - } - - public boolean isInitialized() { - return initialized; - } - } - - static FlowConfig config; - - private static GlobalDatabaseHolder globalDatabaseHolder = new GlobalDatabaseHolder(); - - private static HashSet> loadedModules = new HashSet<>(); - - private static final String DEFAULT_DATABASE_HOLDER_NAME = "GeneratedDatabaseHolder"; - - private static final String DEFAULT_DATABASE_HOLDER_PACKAGE_NAME = - FlowManager.class.getPackage().getName(); - - private static final String DEFAULT_DATABASE_HOLDER_CLASSNAME = - DEFAULT_DATABASE_HOLDER_PACKAGE_NAME + "." + DEFAULT_DATABASE_HOLDER_NAME; - - public static final String DEFAULT_AUTHORITY = "com.dbflow.authority"; - - /** - * Returns the table name for the specific model class - * - * @param table The class that implements {@link Model} - * @return The table name, which can be different than the {@link Model} class name - */ - @SuppressWarnings("unchecked") - @NonNull - public static String getTableName(Class table) { - ModelAdapter modelAdapter = getModelAdapterOrNull(table); - String tableName = null; - if (modelAdapter == null) { - ModelViewAdapter modelViewAdapter = getModelViewAdapterOrNull(table); - if (modelViewAdapter != null) { - tableName = modelViewAdapter.getViewName(); - } else { - throwCannotFindAdapter("ModelAdapter/ModelViewAdapter", table); - } - } else { - tableName = modelAdapter.getTableName(); - } - return tableName; - } - - /** - * @param databaseName The name of the database. Will throw an exception if the database doesn't exist. - * @param tableName The name of the table in the DB. - * @return The associated table class for the specified name. - */ - public static Class getTableClassForName(String databaseName, String tableName) { - DatabaseDefinition databaseDefinition = getDatabase(databaseName); - Class modelClass = databaseDefinition.getModelClassForName(tableName); - if (modelClass == null) { - modelClass = databaseDefinition.getModelClassForName(QueryBuilder.quote(tableName)); - if (modelClass == null) { - throw new IllegalArgumentException(String.format("The specified table %1s was not found. " + - "Did you forget to add the @Table annotation and point it to %1s?", - tableName, databaseName)); - } - } - return modelClass; - } - - /** - * @param databaseClass The class of the database. Will throw an exception if the database doesn't exist. - * @param tableName The name of the table in the DB. - * @return The associated table class for the specified name. - */ - public static Class getTableClassForName(Class databaseClass, String tableName) { - DatabaseDefinition databaseDefinition = getDatabase(databaseClass); - Class modelClass = databaseDefinition.getModelClassForName(tableName); - if (modelClass == null) { - modelClass = databaseDefinition.getModelClassForName(QueryBuilder.quote(tableName)); - if (modelClass == null) { - throw new IllegalArgumentException(String.format("The specified table %1s was not found. " + - "Did you forget to add the @Table annotation and point it to %1s?", - tableName, databaseClass)); - } - } - return modelClass; - } - - /** - * @param table The table to lookup the database for. - * @return the corresponding {@link DatabaseDefinition} for the specified model - */ - @NonNull - public static DatabaseDefinition getDatabaseForTable(Class table) { - checkDatabaseHolder(); - DatabaseDefinition databaseDefinition = globalDatabaseHolder.getDatabaseForTable(table); - if (databaseDefinition == null) { - throw new InvalidDBConfiguration("Model object: " + table.getName() + - " is not registered with a Database. " + "Did you forget an annotation?"); - } - return databaseDefinition; - } - - @NonNull - public static DatabaseDefinition getDatabase(Class databaseClass) { - checkDatabaseHolder(); - DatabaseDefinition databaseDefinition = globalDatabaseHolder.getDatabase(databaseClass); - if (databaseDefinition == null) { - throw new InvalidDBConfiguration("Database: " + databaseClass.getName() + " is not a registered Database. " + - "Did you forget the @Database annotation?"); - } - return databaseDefinition; - } - - @NonNull - public static String getDatabaseName(Class database) { - return getDatabase(database).getDatabaseName(); - } - - @NonNull - public static DatabaseWrapper getWritableDatabaseForTable(Class table) { - return getDatabaseForTable(table).getWritableDatabase(); - } - - /** - * @param databaseName The name of the database. Will throw an exception if the database doesn't exist. - * @return the {@link DatabaseDefinition} for the specified database - */ - @NonNull - public static DatabaseDefinition getDatabase(String databaseName) { - checkDatabaseHolder(); - DatabaseDefinition database = globalDatabaseHolder.getDatabase(databaseName); - - if (database != null) { - return database; - } - - throw new InvalidDBConfiguration("The specified database" + databaseName + " was not found. " + - "Did you forget the @Database annotation?"); - } - - @NonNull - public static DatabaseWrapper getWritableDatabase(String databaseName) { - return getDatabase(databaseName).getWritableDatabase(); - } - - @NonNull - public static DatabaseWrapper getWritableDatabase(Class databaseClass) { - return getDatabase(databaseClass).getWritableDatabase(); - } - - /** - * Loading the module Database holder via reflection. - *

- * It is assumed FlowManager.init() is called by the application that uses the - * module database. This method should only be called if you need to load databases - * that are part of a module. Building once will give you the ability to add the class. - */ - public static void initModule(Class generatedClassName) { - loadDatabaseHolder(generatedClassName); - } - - public static FlowConfig getConfig() { - if (config == null) { - throw new IllegalStateException("Configuration is not initialized. " + - "Please call init(FlowConfig) in your application class."); - } - return config; - } - - /** - * @return The database holder, creating if necessary using reflection. - */ - protected static void loadDatabaseHolder(Class holderClass) { - if (loadedModules.contains(holderClass)) { - return; - } - - try { - // Load the database holder, and add it to the global collection. - DatabaseHolder dbHolder = holderClass.newInstance(); - - if (dbHolder != null) { - globalDatabaseHolder.add(dbHolder); - - // Cache the holder for future reference. - loadedModules.add(holderClass); - } - } catch (Throwable e) { - e.printStackTrace(); - throw new ModuleNotFoundException("Cannot load " + holderClass, e); - } - } - - /** - * Resets all databases and associated files. - */ - public static void reset() { - Set, DatabaseDefinition>> entrySet = globalDatabaseHolder.databaseClassLookupMap.entrySet(); - for (Map.Entry, DatabaseDefinition> value : entrySet) { - value.getValue().reset(); - } - globalDatabaseHolder.reset(); - loadedModules.clear(); - } - - /** - * Close all DB files and resets {@link FlowConfig} and the {@link GlobalDatabaseHolder}. Brings - * DBFlow back to initial application state. - */ - public static synchronized void close() { - Set, DatabaseDefinition>> entrySet = - globalDatabaseHolder.databaseClassLookupMap.entrySet(); - for (Map.Entry, DatabaseDefinition> value : entrySet) { - value.getValue().close(); - } - - config = null; - - globalDatabaseHolder = new GlobalDatabaseHolder(); - loadedModules.clear(); - } - - /** - * Will throw an exception if this class is not initialized yet in {@link #init(FlowConfig)} - * - * @return The shared context. - */ - @NonNull - public static Context getContext() { - if (config == null) { - throw new IllegalStateException("You must provide a valid FlowConfig instance. " + - "We recommend calling init() in your application class."); - } - return config.getContext(); - } - - /** - * Helper method to simplify the {@link #init(FlowConfig)}. Use {@link #init(FlowConfig)} to provide - * more customization. - * - * @param context - should be application context, but not necessary as we retrieve it anyways. - */ - public static void init(@NonNull Context context) { - init(new FlowConfig.Builder(context).build()); - } - - /** - * Initializes DBFlow, loading the main application Database holder via reflection one time only. - * This will trigger all creations, updates, and instantiation for each database defined. - * - * @param flowConfig The configuration instance that will help shape how DBFlow gets constructed. - */ - public static void init(@NonNull FlowConfig flowConfig) { - FlowManager.config = flowConfig; - - try { - //noinspection unchecked - Class defaultHolderClass = (Class) Class.forName(DEFAULT_DATABASE_HOLDER_CLASSNAME); - loadDatabaseHolder(defaultHolderClass); - } catch (ModuleNotFoundException e) { - // Ignore this exception since it means the application does not have its - // own database. The initialization happens because the application is using - // a module that has a database. - FlowLog.log(FlowLog.Level.W, e.getMessage()); - } catch (ClassNotFoundException e) { - // warning if a library uses DBFlow with module support but the app you're using doesn't support it. - FlowLog.log(FlowLog.Level.W, "Could not find the default GeneratedDatabaseHolder"); - } - - if (!flowConfig.databaseHolders().isEmpty()) { - for (Class holder : flowConfig.databaseHolders()) { - loadDatabaseHolder(holder); - } - } - - if (flowConfig.openDatabasesOnInit()) { - List databaseDefinitions = globalDatabaseHolder.getDatabaseDefinitions(); - for (DatabaseDefinition databaseDefinition : databaseDefinitions) { - // triggers open, create, migrations. - databaseDefinition.getWritableDatabase(); - } - } - } - - /** - * @param objectClass A class with an associated type converter. May return null if not found. - * @return The specific {@link TypeConverter} for the specified class. It defines - * how the custom datatype is handled going into and out of the DB. - */ - public static TypeConverter getTypeConverterForClass(Class objectClass) { - checkDatabaseHolder(); - return globalDatabaseHolder.getTypeConverterForClass(objectClass); - } - - // region Getters - - /** - * Release reference to context and {@link FlowConfig} - */ - public static synchronized void destroy() { - Set, DatabaseDefinition>> entrySet = - globalDatabaseHolder.databaseClassLookupMap.entrySet(); - for (Map.Entry, DatabaseDefinition> value : entrySet) { - value.getValue().destroy(); - } - - config = null; - - // Reset the global database holder. - globalDatabaseHolder = new GlobalDatabaseHolder(); - loadedModules.clear(); - } - - /** - * @param modelClass The class that implements {@link Model} to find an adapter for. - * @return The adapter associated with the class. If its not a {@link ModelAdapter}, - * it checks both the {@link ModelViewAdapter} and {@link QueryModelAdapter}. - */ - @SuppressWarnings("unchecked") - @NonNull - public static InstanceAdapter getInstanceAdapter(Class modelClass) { - InstanceAdapter internalAdapter = getModelAdapterOrNull(modelClass); - if (internalAdapter == null) { - internalAdapter = getModelViewAdapterOrNull(modelClass); - if (internalAdapter == null) { - internalAdapter = getQueryModelAdapterOrNull(modelClass); - } - } - - if (internalAdapter == null) { - throwCannotFindAdapter("InstanceAdapter", modelClass); - } - - return internalAdapter; - } - - /** - * @param modelClass The class that implements {@link Model} to find an adapter for. - * @return The adapter associated with the class. If its not a {@link ModelAdapter}, - * it checks both the {@link ModelViewAdapter} and {@link QueryModelAdapter}. - */ - @SuppressWarnings("unchecked") - @NonNull - public static RetrievalAdapter getRetrievalAdapter(Class modelClass) { - RetrievalAdapter retrievalAdapter = getModelAdapterOrNull(modelClass); - if (retrievalAdapter == null) { - retrievalAdapter = getModelViewAdapterOrNull(modelClass); - if (retrievalAdapter == null) { - retrievalAdapter = getQueryModelAdapterOrNull(modelClass); - } - } - if (retrievalAdapter == null) { - throwCannotFindAdapter("RetrievalAdapter", modelClass); - } - - return retrievalAdapter; - } - - - /** - * @param modelClass The class of the table - * @param The class that implements {@link Model} - * @return The associated model adapter (DAO) that is generated from a {@link Table} class. Handles - * interactions with the database. This method is meant for internal usage only. - * We strongly prefer you use the built-in methods associated with {@link Model} and {@link BaseModel}. - */ - @SuppressWarnings("unchecked") - @NonNull - public static ModelAdapter getModelAdapter(Class modelClass) { - final ModelAdapter modelAdapter = getModelAdapterOrNull(modelClass); - if (modelAdapter == null) { - throwCannotFindAdapter("ModelAdapter", modelClass); - } - return modelAdapter; - } - - /** - * Returns the model view adapter for a SQLite VIEW. These are only created with the {@link com.raizlabs.android.dbflow.annotation.ModelView} annotation. - * - * @param modelViewClass The class of the VIEW - * @param The class that extends {@link BaseModelView} - * @return The model view adapter for the specified model view. - */ - @SuppressWarnings("unchecked") - @NonNull - public static ModelViewAdapter getModelViewAdapter( - Class modelViewClass) { - final ModelViewAdapter modelViewAdapter = getModelViewAdapterOrNull(modelViewClass); - if (modelViewAdapter == null) { - throwCannotFindAdapter("ModelViewAdapter", modelViewClass); - } - return modelViewAdapter; - } - - /** - * Returns the query model adapter for an undefined query. These are only created with the {@link TQueryModel} annotation. - * - * @param queryModelClass The class of the query - * @param The class that extends {@link BaseQueryModel} - * @return The query model adapter for the specified model query. - */ - @SuppressWarnings("unchecked") - @NonNull - public static QueryModelAdapter getQueryModelAdapter( - Class queryModelClass) { - final QueryModelAdapter queryModelAdapter = getQueryModelAdapterOrNull(queryModelClass); - if (queryModelAdapter == null) { - throwCannotFindAdapter("QueryModelAdapter", queryModelClass); - } - return queryModelAdapter; - } - - @NonNull - public static ModelNotifier getModelNotifierForTable(Class table) { - return getDatabaseForTable(table).getModelNotifier(); - } - - @NonNull - public static TableNotifierRegister newRegisterForTable(Class table) { - return getModelNotifierForTable(table).newRegister(); - } - - @Nullable - private static ModelAdapter getModelAdapterOrNull(Class modelClass) { - return FlowManager.getDatabaseForTable(modelClass).getModelAdapterForTable(modelClass); - } - - @Nullable - private static ModelViewAdapter getModelViewAdapterOrNull(Class modelClass) { - return FlowManager.getDatabaseForTable(modelClass).getModelViewAdapterForTable(modelClass); - } - - @Nullable - private static QueryModelAdapter getQueryModelAdapterOrNull(Class modelClass) { - return FlowManager.getDatabaseForTable(modelClass).getQueryModelAdapterForQueryClass(modelClass); - } - - /** - * @param databaseName The name of the database. Will throw an exception if the database doesn't exist. - * @return The map of migrations for the specified database. - */ - static Map> getMigrations(String databaseName) { - return getDatabase(databaseName).getMigrations(); - } - - /** - * Checks a standard database helper for integrity using quick_check(1). - * - * @param databaseName The name of the database to check. Will thrown an exception if it does not exist. - * @return true if it's integrity is OK. - */ - public static boolean isDatabaseIntegrityOk(String databaseName) { - return getDatabase(databaseName).getHelper().isDatabaseIntegrityOk(); - } - - private static void throwCannotFindAdapter(String type, Class clazz) { - throw new IllegalArgumentException("Cannot find " + type + " for " + clazz + ". Ensure " + - "the class is annotated with proper annotation."); - } - - private static void checkDatabaseHolder() { - if (!globalDatabaseHolder.isInitialized()) { - throw new IllegalStateException("The global database holder is not initialized. Ensure you call " + - "FlowManager.init() before accessing the database."); - } - } - - // endregion - - /** - * Exception thrown when a database holder cannot load the database holder - * for a module. - */ - public static class ModuleNotFoundException extends RuntimeException { - public ModuleNotFoundException() { - } - - public ModuleNotFoundException(String detailMessage) { - super(detailMessage); - } - - public ModuleNotFoundException(String detailMessage, Throwable throwable) { - super(detailMessage, throwable); - } - - public ModuleNotFoundException(Throwable throwable) { - super(throwable); - } - } - -} \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/TableConfig.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/config/TableConfig.java deleted file mode 100644 index c01aefa7c..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/TableConfig.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.raizlabs.android.dbflow.config; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.queriable.ListModelLoader; -import com.raizlabs.android.dbflow.sql.queriable.SingleModelLoader; -import com.raizlabs.android.dbflow.sql.saveable.ModelSaver; -import com.raizlabs.android.dbflow.structure.ModelAdapter; - -import java.util.List; - -/** - * Description: Represents certain table configuration options. This allows you to easily specify - * certain configuration options for a table. - */ -public final class TableConfig { - - public static TableConfig.Builder builder(Class tableClass) { - return new TableConfig.Builder<>(tableClass); - } - - private final Class tableClass; - private final ModelSaver modelSaver; - private final SingleModelLoader singleModelLoader; - private final ListModelLoader listModelLoader; - - TableConfig(Builder builder) { - tableClass = builder.tableClass; - modelSaver = builder.modelAdapterModelSaver; - singleModelLoader = builder.singleModelLoader; - listModelLoader = builder.listModelLoader; - } - - @NonNull - public Class tableClass() { - return tableClass; - } - - @Nullable - public ModelSaver modelSaver() { - return modelSaver; - } - - @Nullable - public ListModelLoader listModelLoader() { - return listModelLoader; - } - - @Nullable - public SingleModelLoader singleModelLoader() { - return singleModelLoader; - } - - public static final class Builder { - - final Class tableClass; - ModelSaver modelAdapterModelSaver; - SingleModelLoader singleModelLoader; - ListModelLoader listModelLoader; - - public Builder(@NonNull Class tableClass) { - this.tableClass = tableClass; - } - - /** - * Define how the {@link ModelAdapter} saves data into the DB from its associated {@link TModel}. This - * will override the default. - */ - @NonNull - public Builder modelAdapterModelSaver(@NonNull ModelSaver modelSaver) { - this.modelAdapterModelSaver = modelSaver; - return this; - } - - /** - * Define how the table loads single models. This will override the default. - */ - @NonNull - public Builder singleModelLoader(@NonNull SingleModelLoader singleModelLoader) { - this.singleModelLoader = singleModelLoader; - return this; - } - - /** - * Define how the table loads a {@link List} of items. This will override the default. - */ - @NonNull - public Builder listModelLoader(@NonNull ListModelLoader listModelLoader) { - this.listModelLoader = listModelLoader; - return this; - } - - /** - * @return A new {@link TableConfig}. Subsequent calls to this method produce a new instance - * of {@link TableConfig}. - */ - @NonNull - public TableConfig build() { - return new TableConfig<>(this); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorIterator.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorIterator.java deleted file mode 100644 index e6a1347ae..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorIterator.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.raizlabs.android.dbflow.list; - -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.util.ConcurrentModificationException; -import java.util.ListIterator; - -/** - * Description: Provides iteration capabilities to a {@link FlowCursorList}. - */ -public class FlowCursorIterator implements ListIterator, AutoCloseable { - - private final IFlowCursorIterator cursorList; - private long reverseIndex; - private long startingCount; - private long count; - - public FlowCursorIterator(@NonNull IFlowCursorIterator cursorList) { - this(cursorList, 0, cursorList.getCount()); - } - - public FlowCursorIterator(@NonNull IFlowCursorIterator cursorList, int startingLocation) { - this(cursorList, startingLocation, cursorList.getCount() - startingLocation); - } - - public FlowCursorIterator(@NonNull IFlowCursorIterator cursorList, int startingLocation, - long count) { - this.cursorList = cursorList; - this.count = count; - Cursor cursor = cursorList.cursor(); - if (cursor != null) { - // request larger than actual count. - if (this.count > cursor.getCount() - startingLocation) { - this.count = cursor.getCount() - startingLocation; - } - - cursor.moveToPosition(startingLocation - 1); - startingCount = cursorList.getCount(); - reverseIndex = this.count; - reverseIndex -= startingLocation; - - if (reverseIndex < 0) { - reverseIndex = 0; - } - } - } - - @Override - public void close() throws Exception { - cursorList.close(); - } - - @Override - public void add(@Nullable TModel object) { - throw new UnsupportedOperationException("Cursor Iterator: Cannot add a model in the iterator"); - } - - @Override - public boolean hasNext() { - checkSizes(); - return reverseIndex > 0; - } - - @Override - public boolean hasPrevious() { - checkSizes(); - return reverseIndex < count; - } - - @Nullable - @Override - public TModel next() { - checkSizes(); - TModel item = cursorList.getItem(count - reverseIndex); - reverseIndex--; - return item; - } - - @Override - public int nextIndex() { - return (int) (reverseIndex + 1); - } - - @Nullable - @Override - public TModel previous() { - checkSizes(); - TModel item = cursorList.getItem(count - reverseIndex); - reverseIndex++; - return item; - } - - @Override - public int previousIndex() { - return (int) reverseIndex; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Cursor Iterator: cannot remove from an active Iterator "); - } - - @Override - public void set(@Nullable TModel object) { - throw new UnsupportedOperationException("Cursor Iterator: cannot set on an active Iterator "); - } - - private void checkSizes() { - if (startingCount != cursorList.getCount()) { - throw new ConcurrentModificationException("Cannot change Cursor data during iteration."); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorList.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorList.java deleted file mode 100644 index 411ab6eac..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorList.java +++ /dev/null @@ -1,361 +0,0 @@ -package com.raizlabs.android.dbflow.list; - -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.widget.ListView; - -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.InstanceAdapter; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.cache.ModelCache; -import com.raizlabs.android.dbflow.structure.cache.ModelLruCache; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -/** - * Description: A non-modifiable, cursor-backed list that you can use in {@link ListView} or other data sources. - */ -public class FlowCursorList implements - Iterable, IFlowCursorIterator { - - /** - * Interface for callbacks when cursor gets refreshed. - */ - public interface OnCursorRefreshListener { - - /** - * Callback when cursor refreshes. - * - * @param cursorList The object that changed. - */ - void onCursorRefreshed(@NonNull FlowCursorList cursorList); - } - - /** - * The default size of the cache if cache size is 0 or not specified. - */ - public static final int DEFAULT_CACHE_SIZE = 50; - - /** - * Minimum size that we make the cache (if size is supported in cache) - */ - public static final int MIN_CACHE_SIZE = 20; - - @Nullable - private FlowCursor cursor; - - private Class table; - private ModelCache modelCache; - private boolean cacheModels; - - @Nullable - private ModelQueriable modelQueriable; - - private InstanceAdapter instanceAdapter; - - private final java.util.Set> cursorRefreshListenerSet = new HashSet<>(); - - private FlowCursorList(final Builder builder) { - table = builder.modelClass; - modelQueriable = builder.modelQueriable; - if (builder.modelQueriable == null) { - cursor = builder.cursor; - // no cursor or queriable, we formulate query from table data. - if (cursor == null) { - modelQueriable = SQLite.select().from(table); - cursor = modelQueriable.query(); - } - } else { - cursor = builder.modelQueriable.query(); - } - cacheModels = builder.cacheModels; - if (cacheModels) { - modelCache = builder.modelCache; - if (modelCache == null) { - // new cache with default size - modelCache = ModelLruCache.newInstance(0); - } - } - instanceAdapter = FlowManager.getInstanceAdapter(builder.modelClass); - - setCacheModels(cacheModels); - } - - @NonNull - InstanceAdapter getInstanceAdapter() { - return instanceAdapter; - } - - @NonNull - ModelAdapter getModelAdapter() { - return (ModelAdapter) instanceAdapter; - } - - @NonNull - @Override - public FlowCursorIterator iterator() { - return new FlowCursorIterator<>(this); - } - - @NonNull - @Override - public FlowCursorIterator iterator(int startingLocation, long limit) { - return new FlowCursorIterator<>(this, startingLocation, limit); - } - - /** - * Register listener for when cursor refreshes. - */ - public void addOnCursorRefreshListener(@NonNull OnCursorRefreshListener onCursorRefreshListener) { - synchronized (cursorRefreshListenerSet) { - cursorRefreshListenerSet.add(onCursorRefreshListener); - } - } - - public void removeOnCursorRefreshListener(@NonNull OnCursorRefreshListener onCursorRefreshListener) { - synchronized (cursorRefreshListenerSet) { - cursorRefreshListenerSet.remove(onCursorRefreshListener); - } - } - - /** - * Sets this list to cache models. If set to false, it will immediately clear the cache for you. - * - * @param cacheModels true, will cache models. If false, any and future caching is cleared. - * @deprecated use {@link Builder#cacheModels(boolean)}, {@link Builder#modelCache()} - */ - void setCacheModels(boolean cacheModels) { - this.cacheModels = cacheModels; - if (!cacheModels) { - clearCache(); - } - } - - /** - * Clears the {@link TModel} cache if we use a cache. - */ - public void clearCache() { - if (cacheModels) { - modelCache.clear(); - } - } - - /** - * Refreshes the data backing this list, and destroys the Model cache. - */ - public synchronized void refresh() { - warnEmptyCursor(); - if (cursor != null) { - cursor.close(); - } - if (modelQueriable == null) { - throw new IllegalStateException("Cannot refresh this FlowCursorList. This list was instantiated from a Cursor. Once closed, we cannot reopen " + - "it. Construct a new instance and swap with this instance."); - } - cursor = modelQueriable.query(); - - if (cacheModels) { - modelCache.clear(); - setCacheModels(true); - } - - synchronized (cursorRefreshListenerSet) { - for (OnCursorRefreshListener listener : cursorRefreshListenerSet) { - listener.onCursorRefreshed(this); - } - } - } - - @Nullable - public ModelQueriable modelQueriable() { - return modelQueriable; - } - - /** - * Returns a model at the specified position. If we are using the cache and it does not contain a model - * at that position, we move the cursor to the specified position and construct the {@link TModel}. - * - * @param position The row number in the {@link android.database.Cursor} to look at - * @return The {@link TModel} converted from the cursor - */ - @Nullable - @Override - public TModel getItem(long position) { - throwIfCursorClosed(); - warnEmptyCursor(); - - TModel model = null; - if (cacheModels) { - model = modelCache.get(position); - if (model == null && cursor != null && cursor.moveToPosition((int) position)) { - model = instanceAdapter.getSingleModelLoader().convertToData(cursor, null, false); - modelCache.addModel(position, model); - } - } else if (cursor != null && cursor.moveToPosition((int) position)) { - model = instanceAdapter.getSingleModelLoader().convertToData(cursor, null, false); - } - return model; - } - - /** - * @return the full, converted {@link TModel} list from the database on this list. For large - * data sets that require a large conversion, consider calling this on a BG thread. - */ - @NonNull - public List getAll() { - throwIfCursorClosed(); - warnEmptyCursor(); - if (!cacheModels) { - return cursor == null ? new ArrayList() : - FlowManager.getModelAdapter(table).getListModelLoader().convertToData(cursor, null); - } else { - List list = new ArrayList<>(); - for (TModel model : this) { - list.add(model); - } - return list; - } - } - - /** - * @return the count of rows on this database query list. - */ - public boolean isEmpty() { - throwIfCursorClosed(); - warnEmptyCursor(); - return getCount() == 0; - } - - /** - * @return the count of the rows in the {@link android.database.Cursor} backed by this list. - */ - @Override - public long getCount() { - throwIfCursorClosed(); - warnEmptyCursor(); - return cursor != null ? cursor.getCount() : 0; - } - - @NonNull - public ModelCache modelCache() { - return modelCache; - } - - public boolean cachingEnabled() { - return cacheModels; - } - - /** - * Closes the cursor backed by this list - */ - @Override - public void close() { - warnEmptyCursor(); - if (cursor != null) { - cursor.close(); - } - cursor = null; - } - - @Override - @Nullable - public Cursor cursor() { - throwIfCursorClosed(); - warnEmptyCursor(); - return cursor; - } - - @NonNull - public Class table() { - return table; - } - - private void throwIfCursorClosed() { - if (cursor != null && cursor.isClosed()) { - throw new IllegalStateException("Cursor has been closed for FlowCursorList"); - } - } - - private void warnEmptyCursor() { - if (cursor == null) { - FlowLog.log(FlowLog.Level.W, "Cursor was null for FlowCursorList"); - } - } - - /** - * @return A new {@link Builder} that contains the same cache, query statement, and other - * underlying data, but allows for modification. - */ - @NonNull - public Builder newBuilder() { - return new Builder<>(table) - .modelQueriable(modelQueriable) - .cursor(cursor) - .cacheModels(cacheModels) - .modelCache(modelCache); - } - - /** - * Provides easy way to construct a {@link FlowCursorList}. - * - * @param - */ - public static class Builder { - - private final Class modelClass; - private FlowCursor cursor; - private ModelQueriable modelQueriable; - private boolean cacheModels = true; - private ModelCache modelCache; - - public Builder(@NonNull Class modelClass) { - this.modelClass = modelClass; - } - - public Builder(@NonNull ModelQueriable modelQueriable) { - this.modelClass = modelQueriable.getTable(); - modelQueriable(modelQueriable); - } - - @NonNull - public Builder cursor(@Nullable Cursor cursor) { - if (cursor != null) { - this.cursor = FlowCursor.from(cursor); - } - return this; - } - - @NonNull - public Builder modelQueriable(@Nullable ModelQueriable modelQueriable) { - this.modelQueriable = modelQueriable; - return this; - } - - @NonNull - public Builder cacheModels(boolean cacheModels) { - this.cacheModels = cacheModels; - return this; - } - - @NonNull - public Builder modelCache(@Nullable ModelCache modelCache) { - this.modelCache = modelCache; - if (modelCache != null) { - cacheModels(true); - } - return this; - } - - @NonNull - public FlowCursorList build() { - return new FlowCursorList<>(this); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowQueryList.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowQueryList.java deleted file mode 100644 index 3cf92935b..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowQueryList.java +++ /dev/null @@ -1,750 +0,0 @@ -package com.raizlabs.android.dbflow.list; - -import android.annotation.TargetApi; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.StringUtils; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.list.FlowCursorList.OnCursorRefreshListener; -import com.raizlabs.android.dbflow.runtime.FlowContentObserver; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.InstanceAdapter; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.cache.ModelCache; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.transaction.DefaultTransactionQueue; -import com.raizlabs.android.dbflow.structure.database.transaction.ProcessModelTransaction; -import com.raizlabs.android.dbflow.structure.database.transaction.QueryTransaction; -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction; - -import java.util.Collection; -import java.util.List; -import java.util.ListIterator; - -/** - * Description: Operates very similiar to a {@link java.util.List} except its backed by a table cursor. All of - * the {@link java.util.List} modifications default to the main thread, but it can be set to - * run on the {@link DefaultTransactionQueue}. Register a {@link Transaction.Success} - * on this list to know when the results complete. NOTE: any modifications to this list will be reflected - * on the underlying table. - */ -public class FlowQueryList extends FlowContentObserver - implements List, IFlowCursorIterator { - - private static final Handler REFRESH_HANDLER = new Handler(Looper.myLooper()); - - /** - * Holds the table cursor - */ - private final FlowCursorList internalCursorList; - private final Transaction.Success successCallback; - private final Transaction.Error errorCallback; - - /** - * If true, we will make all modifications on the {@link DefaultTransactionQueue}, else - * we will run it on the main thread. - */ - private boolean transact = false; - - private boolean changeInTransaction = false; - - private boolean pendingRefresh = false; - - - private FlowQueryList(Builder builder) { - super(StringUtils.isNotNullOrEmpty(builder.contentAuthority) - ? builder.contentAuthority - : FlowManager.DEFAULT_AUTHORITY); - transact = builder.transact; - changeInTransaction = builder.changeInTransaction; - successCallback = builder.success; - errorCallback = builder.error; - internalCursorList = new FlowCursorList.Builder<>(builder.table) - .cursor(builder.cursor) - .cacheModels(builder.cacheModels) - .modelQueriable(builder.modelQueriable) - .modelCache(builder.modelCache) - .build(); - } - - /** - * Registers the list for model change events. Internally this refreshes the underlying {@link FlowCursorList}. Call - * {@link #beginTransaction()} to bunch up calls to model changes and then {@link #endTransactionAndNotify()} to dispatch - * and refresh this list when completed. - */ - public void registerForContentChanges(@NonNull Context context) { - super.registerForContentChanges(context, internalCursorList.table()); - } - - public void addOnCursorRefreshListener(@NonNull OnCursorRefreshListener onCursorRefreshListener) { - internalCursorList.addOnCursorRefreshListener(onCursorRefreshListener); - } - - public void removeOnCursorRefreshListener(@NonNull OnCursorRefreshListener onCursorRefreshListener) { - internalCursorList.removeOnCursorRefreshListener(onCursorRefreshListener); - } - - @Override - public void registerForContentChanges(Context context, Class table) { - throw new RuntimeException( - "This method is not to be used in the FlowQueryList. We should only ever receive" + - " notifications for one class here. Call registerForContentChanges(Context) instead"); - } - - @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - if (!isInTransaction) { - refreshAsync(); - } else { - changeInTransaction = true; - } - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - @Override - public void onChange(boolean selfChange, Uri uri) { - super.onChange(selfChange, uri); - if (!isInTransaction) { - refreshAsync(); - } else { - changeInTransaction = true; - } - } - - /** - * @return a mutable list that does not reflect changes on the underlying DB. - */ - @NonNull - public List getCopy() { - return internalCursorList.getAll(); - } - - @NonNull - public FlowCursorList cursorList() { - return internalCursorList; - } - - @Nullable - public Transaction.Error error() { - return errorCallback; - } - - @Nullable - public Transaction.Success success() { - return successCallback; - } - - public boolean changeInTransaction() { - return changeInTransaction; - } - - public boolean transact() { - return transact; - } - - @NonNull - ModelAdapter getModelAdapter() { - return internalCursorList.getModelAdapter(); - } - - @NonNull - InstanceAdapter getInstanceAdapter() { - return internalCursorList.getInstanceAdapter(); - } - - /** - * @return Constructs a new {@link Builder} that reuses the underlying {@link Cursor}, cache, - * callbacks, and other properties. - */ - @NonNull - public Builder newBuilder() { - return new Builder<>(internalCursorList) - .success(successCallback) - .error(errorCallback) - .changeInTransaction(changeInTransaction) - .transact(transact); - } - - /** - * Refreshes the content backing this list. - */ - public void refresh() { - internalCursorList.refresh(); - } - - /** - * Will refresh content at a slightly later time, and multiple subsequent calls to this method within - * a short period of time will be combined into one call. - */ - public void refreshAsync() { - synchronized (this) { - if (pendingRefresh) { - return; - } - pendingRefresh = true; - } - REFRESH_HANDLER.post(refreshRunnable); - } - - @Override - public void endTransactionAndNotify() { - if (changeInTransaction) { - changeInTransaction = false; - refresh(); - } - super.endTransactionAndNotify(); - } - - /** - * Adds an item to this table, but does not allow positonal insertion. Same as calling - * {@link #add(TModel)} - * - * @param location Not used. - * @param model The model to save - */ - @Override - public void add(int location, @Nullable TModel model) { - add(model); - } - - /** - * Adds an item to this table - * - * @param model The model to save - * @return always true - */ - @Override - public boolean add(@Nullable TModel model) { - if (model != null) { - Transaction transaction = FlowManager.getDatabaseForTable(internalCursorList.table()) - .beginTransactionAsync(new ProcessModelTransaction.Builder<>(saveModel) - .add(model).build()) - .error(internalErrorCallback) - .success(internalSuccessCallback).build(); - - if (transact) { - transaction.execute(); - } else { - transaction.executeSync(); - } - return true; - } else { - return false; - } - } - - /** - * Adds all items to this table, but - * does not allow positional insertion. Same as calling {@link #addAll(java.util.Collection)} - * - * @param location Not used. - * @param collection The list of items to add to the table - * @return always true - */ - @Override - public boolean addAll(int location, @NonNull Collection collection) { - return addAll(collection); - } - - /** - * Adds all items to this table. - * - * @param collection The list of items to add to the table - * @return always true - */ - @SuppressWarnings("unchecked") - @Override - public boolean addAll(@NonNull Collection collection) { - // cast to normal collection, we do not want subclasses of this table saved - final Collection tmpCollection = (Collection) collection; - - Transaction transaction = FlowManager.getDatabaseForTable(internalCursorList.table()) - .beginTransactionAsync(new ProcessModelTransaction.Builder<>(saveModel) - .addAll(tmpCollection).build()) - .error(internalErrorCallback) - .success(internalSuccessCallback).build(); - - if (transact) { - transaction.execute(); - } else { - transaction.executeSync(); - } - return true; - } - - /** - * Deletes all items from the table. Be careful as this will clear data! - */ - @Override - public void clear() { - Transaction transaction = FlowManager.getDatabaseForTable(internalCursorList.table()) - .beginTransactionAsync(new QueryTransaction.Builder<>( - SQLite.delete().from(internalCursorList.table())).build()) - .error(internalErrorCallback) - .success(internalSuccessCallback) - .build(); - - if (transact) { - transaction.execute(); - } else { - transaction.executeSync(); - } - } - - /** - * Checks to see if the table contains the object only if its a {@link TModel} - * - * @param object A model class. For interface purposes, this must be an Object. - * @return always false if its anything other than the current table. True if {@link com.raizlabs.android.dbflow.structure.Model#exists()} passes. - */ - @SuppressWarnings("unchecked") - @Override - public boolean contains(@Nullable Object object) { - boolean contains = false; - if (object != null && internalCursorList.table().isAssignableFrom(object.getClass())) { - TModel model = ((TModel) object); - contains = internalCursorList.getInstanceAdapter().exists(model); - } - - return contains; - } - - /** - * If the collection is null or empty, we return false. - * - * @param collection The collection to check if all exist within the table. - * @return true if all items exist in table, false if at least one fails. - */ - @Override - public boolean containsAll(@NonNull Collection collection) { - boolean contains = !(collection.isEmpty()); - if (contains) { - for (Object o : collection) { - if (!contains(o)) { - contains = false; - break; - } - } - } - return contains; - } - - @Override - public long getCount() { - return internalCursorList.getCount(); - } - - @Nullable - @Override - public TModel getItem(long position) { - return internalCursorList.getItem(position); - } - - @Nullable - @Override - public Cursor cursor() { - return internalCursorList.cursor(); - } - - /** - * Returns the item from the backing {@link FlowCursorList}. First call - * will load the model from the cursor, while subsequent calls will use the cache. - * - * @param row the row from the internal {@link FlowCursorList} query that we use. - * @return A model converted from the internal {@link FlowCursorList}. For - * performance improvements, ensure caching is turned on. - */ - @Nullable - @Override - public TModel get(int row) { - return internalCursorList.getItem(row); - } - - @Override - public int indexOf(Object object) { - throw new UnsupportedOperationException( - "We cannot determine which index in the table this item exists at efficiently"); - } - - @Override - public boolean isEmpty() { - return internalCursorList.isEmpty(); - } - - /** - * @return An iterator from {@link FlowCursorList#getAll()}. - * Be careful as this method will convert all data under this table into a list of {@link TModel} in the UI thread. - */ - @NonNull - @Override - public FlowCursorIterator iterator() { - return new FlowCursorIterator<>(this); - } - - @NonNull - @Override - public FlowCursorIterator iterator(int startingLocation, long limit) { - return new FlowCursorIterator<>(this, startingLocation, limit); - } - - @Override - public int lastIndexOf(Object object) { - throw new UnsupportedOperationException( - "We cannot determine which index in the table this item exists at efficiently"); - } - - /** - * @return A list iterator from the {@link FlowCursorList#getAll()}. - * Be careful as this method will convert all data under this table into a list of {@link TModel} in the UI thread. - */ - @NonNull - @Override - public ListIterator listIterator() { - return new FlowCursorIterator<>(this); - } - - /** - * @param location The index to start the iterator. - * @return A list iterator from the {@link FlowCursorList#getAll()}. - * Be careful as this method will convert all data under this table into a list of {@link TModel} in the UI thread. - */ - @NonNull - @Override - public ListIterator listIterator(int location) { - return new FlowCursorIterator<>(this, location); - } - - /** - * Deletes a {@link TModel} at a specific position within the stored {@link Cursor}. - * If {@link #transact} is true, the delete does not happen immediately. Avoid using this operation - * many times. If you need to remove multiple, use {@link #removeAll(Collection)} - * - * @param location The location within the table to remove the item from - * @return The removed item. - */ - @Override - public TModel remove(int location) { - TModel model = internalCursorList.getItem(location); - - Transaction transaction = FlowManager.getDatabaseForTable(internalCursorList.table()) - .beginTransactionAsync(new ProcessModelTransaction.Builder<>(deleteModel) - .add(model).build()) - .error(internalErrorCallback) - .success(internalSuccessCallback).build(); - - if (transact) { - transaction.execute(); - } else { - transaction.executeSync(); - } - return model; - } - - /** - * Removes an item from this table on the {@link DefaultTransactionQueue} if - * {@link #transact} is true. - * - * @param object A model class. For interface purposes, this must be an Object. - * @return true if the item was removed. Always false if the object is not from the same table as this list. - */ - @SuppressWarnings("unchecked") - @Override - public boolean remove(Object object) { - boolean removed = false; - - // if its a ModelClass - if (internalCursorList.table().isAssignableFrom(object.getClass())) { - TModel model = ((TModel) object); - Transaction transaction = FlowManager.getDatabaseForTable(internalCursorList.table()) - .beginTransactionAsync(new ProcessModelTransaction.Builder<>(deleteModel) - .add(model).build()) - .error(internalErrorCallback) - .success(internalSuccessCallback).build(); - - if (transact) { - transaction.execute(); - } else { - transaction.executeSync(); - } - removed = true; - } - - return removed; - } - - /** - * Removes all items from this table in one transaction based on the list passed. This may happen in the background - * if {@link #transact} is true. - * - * @param collection The collection to remove. - * @return Always true. Will cause a {@link ClassCastException} if the collection is not of type {@link TModel} - */ - @SuppressWarnings("unchecked") - @Override - public boolean removeAll(@NonNull Collection collection) { - - // if its a ModelClass - Collection modelCollection = (Collection) collection; - Transaction transaction = FlowManager.getDatabaseForTable(internalCursorList.table()) - .beginTransactionAsync(new ProcessModelTransaction.Builder<>(deleteModel) - .addAll(modelCollection).build()) - .error(internalErrorCallback) - .success(internalSuccessCallback).build(); - - if (transact) { - transaction.execute(); - } else { - transaction.executeSync(); - } - - return true; - } - - /** - * Retrieves the full list of {@link TModel} items from the table, removes these from the list, and - * then deletes the remaining members. This is not that efficient. - * - * @param collection The collection if models to keep in the table. - * @return Always true. - */ - @Override - public boolean retainAll(@NonNull Collection collection) { - List tableList = internalCursorList.getAll(); - tableList.removeAll(collection); - Transaction transaction = FlowManager.getDatabaseForTable(internalCursorList.table()) - .beginTransactionAsync(new ProcessModelTransaction.Builder<>(tableList, deleteModel) - .build()) - .error(internalErrorCallback) - .success(internalSuccessCallback).build(); - - if (transact) { - transaction.execute(); - } else { - transaction.executeSync(); - } - return true; - } - - /** - * Will not use the index, rather just call a {@link Model#update()} - * - * @param location Not used. - * @param object The object to update - * @return the updated model. - */ - @Override - public TModel set(int location, TModel object) { - return set(object); - } - - /** - * Updates a Model {@link Model#update()} . If {@link #transact} - * is true, this update happens in the BG, otherwise it happens immediately. - * - * @param object The object to update - * @return The updated model. - */ - public TModel set(TModel object) { - Transaction transaction = FlowManager.getDatabaseForTable(internalCursorList.table()) - .beginTransactionAsync(new ProcessModelTransaction.Builder<>(updateModel) - .add(object) - .build()) - .error(internalErrorCallback) - .success(internalSuccessCallback).build(); - - if (transact) { - transaction.execute(); - } else { - transaction.executeSync(); - } - return object; - } - - @Override - public int size() { - return (int) internalCursorList.getCount(); - } - - @NonNull - @Override - public List subList(int start, int end) { - List tableList = internalCursorList.getAll(); - return tableList.subList(start, end); - } - - @NonNull - @Override - public Object[] toArray() { - List tableList = internalCursorList.getAll(); - return tableList.toArray(); - } - - @NonNull - @Override - public T[] toArray(T[] array) { - List tableList = internalCursorList.getAll(); - return tableList.toArray(array); - } - - @Override - public void close() { - internalCursorList.close(); - } - - private final ProcessModelTransaction.ProcessModel saveModel = - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().save(model); - } - }; - - private final ProcessModelTransaction.ProcessModel updateModel = - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().update(model); - } - }; - - private final ProcessModelTransaction.ProcessModel deleteModel = - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().delete(model); - } - }; - - private final Transaction.Error internalErrorCallback = new Transaction.Error() { - @Override - public void onError(@NonNull Transaction transaction, @NonNull Throwable error) { - - if (errorCallback != null) { - errorCallback.onError(transaction, error); - } - } - }; - - private final Transaction.Success internalSuccessCallback = new Transaction.Success() { - @Override - public void onSuccess(@NonNull Transaction transaction) { - if (!isInTransaction) { - refreshAsync(); - } else { - changeInTransaction = true; - } - - if (successCallback != null) { - successCallback.onSuccess(transaction); - } - } - }; - - private final Runnable refreshRunnable = new Runnable() { - @Override - public void run() { - synchronized (this) { - pendingRefresh = false; - } - refresh(); - } - }; - - public static class Builder { - - private final Class table; - - private boolean transact; - private boolean changeInTransaction; - private Cursor cursor; - private boolean cacheModels = true; - private ModelQueriable modelQueriable; - private ModelCache modelCache; - - private Transaction.Success success; - private Transaction.Error error; - - private String contentAuthority; - - private Builder(FlowCursorList cursorList) { - table = cursorList.table(); - cursor = cursorList.cursor(); - cacheModels = cursorList.cachingEnabled(); - modelQueriable = cursorList.modelQueriable(); - modelCache = cursorList.modelCache(); - } - - public Builder(Class table) { - this.table = table; - } - - public Builder(@NonNull ModelQueriable modelQueriable) { - this(modelQueriable.getTable()); - modelQueriable(modelQueriable); - } - - public Builder cursor(Cursor cursor) { - this.cursor = cursor; - return this; - } - - public Builder modelQueriable(ModelQueriable modelQueriable) { - this.modelQueriable = modelQueriable; - return this; - } - - public Builder transact(boolean transact) { - this.transact = transact; - return this; - } - - public Builder modelCache(ModelCache modelCache) { - this.modelCache = modelCache; - return this; - } - - public Builder contentAuthority(String contentAuthority) { - this.contentAuthority = contentAuthority; - return this; - } - - /** - * If true, when an operation occurs when we call endTransactionAndNotify, we refresh content. - */ - public Builder changeInTransaction(boolean changeInTransaction) { - this.changeInTransaction = changeInTransaction; - return this; - } - - public Builder cacheModels(boolean cacheModels) { - this.cacheModels = cacheModels; - return this; - } - - public Builder success(Transaction.Success success) { - this.success = success; - return this; - } - - public Builder error(Transaction.Error error) { - this.error = error; - return this; - } - - public FlowQueryList build() { - return new FlowQueryList<>(this); - } - } - - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/IFlowCursorIterator.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/IFlowCursorIterator.java deleted file mode 100644 index 31b890603..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/IFlowCursorIterator.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.raizlabs.android.dbflow.list; - -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.io.Closeable; -import java.io.IOException; - -/** - * Description: Simple interface that allows you to iterate a {@link Cursor}. - */ -public interface IFlowCursorIterator extends Closeable { - - /** - * @return Count of the {@link Cursor}. - */ - long getCount(); - - /** - * @param position The position within the {@link Cursor} to retrieve and convert into a {@link TModel} - */ - @Nullable - TModel getItem(long position); - - /** - * @return The cursor. - */ - @Nullable - Cursor cursor(); - - /** - * @return Can iterate the {@link Cursor}. - */ - @NonNull - FlowCursorIterator iterator(); - - /** - * @return Can iterate the {@link Cursor}. Specifies a starting location + count limit of results. - */ - @NonNull - FlowCursorIterator iterator(int startingLocation, long limit); - - @Override - void close() throws IOException; -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/BaseContentProvider.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/BaseContentProvider.java deleted file mode 100644 index c15a761bf..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/BaseContentProvider.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.net.Uri; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.DatabaseHolder; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.sql.language.property.Property; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.transaction.ITransaction; - -/** - * Description: The base provider class that {@link com.raizlabs.android.dbflow.annotation.provider.ContentProvider} - * extend when generated. - */ -public abstract class BaseContentProvider extends ContentProvider { - - protected Class moduleClass; - - /** - * Converts the column into a {@link Property}. This exists since the property method is static and cannot - * be referenced easily. - */ - public interface PropertyConverter { - IProperty fromName(String columnName); - } - - protected BaseContentProvider() { - } - - protected BaseContentProvider(@NonNull Class databaseHolderClass) { - this.moduleClass = databaseHolderClass; - } - - protected DatabaseDefinition database; - - @Override - public boolean onCreate() { - // If this is a module, then we need to initialize the module as part - // of the creation process. We can assume the framework has been general - // framework has been initialized. - if (moduleClass != null) { - FlowManager.initModule(moduleClass); - } else if (getContext() != null) { - FlowManager.init(getContext()); - } - - return true; - } - - @Override - public int bulkInsert(@NonNull final Uri uri, @NonNull final ContentValues[] values) { - final int[] count = {0}; - getDatabase().executeTransaction(new ITransaction() { - @Override - public void execute(DatabaseWrapper databaseWrapper) { - for (ContentValues contentValues : values) { - count[0] += bulkInsert(uri, contentValues); - } - } - }); - //noinspection ConstantConditions - getContext().getContentResolver().notifyChange(uri, null); - return count[0]; - } - - protected abstract String getDatabaseName(); - - protected abstract int bulkInsert(@NonNull Uri uri, @NonNull ContentValues contentValues); - - @NonNull - protected DatabaseDefinition getDatabase() { - if (database == null) { - database = FlowManager.getDatabase(getDatabaseName()); - } - return database; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/BaseTransactionManager.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/BaseTransactionManager.java deleted file mode 100644 index ffdb2f7c1..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/BaseTransactionManager.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.structure.database.transaction.ITransactionQueue; -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction; - -/** - * Description: The base implementation of Transaction manager. - */ -public abstract class BaseTransactionManager { - - private final ITransactionQueue transactionQueue; - private DBBatchSaveQueue saveQueue; - - public BaseTransactionManager(@NonNull ITransactionQueue transactionQueue, - @NonNull DatabaseDefinition databaseDefinition) { - this.transactionQueue = transactionQueue; - saveQueue = new DBBatchSaveQueue(databaseDefinition); - checkQueue(); - } - - @NonNull - public DBBatchSaveQueue getSaveQueue() { - try { - if (!saveQueue.isAlive()) { - saveQueue.start(); - } - } catch (IllegalThreadStateException i) { - FlowLog.logError(i); // if queue is alive, will throw error. might occur in multithreading. - } - return saveQueue; - } - - @NonNull - public ITransactionQueue getQueue() { - return transactionQueue; - } - - /** - * Checks if queue is running. If not, should be started here. - */ - public void checkQueue() { - getQueue().startIfNotAlive(); - } - - /** - * Stops the queue this manager contains. - */ - public void stopQueue() { - getQueue().quit(); - } - - /** - * Adds a transaction to the {@link ITransactionQueue}. - * - * @param transaction The transaction to add. - */ - public void addTransaction(@NonNull Transaction transaction) { - getQueue().add(transaction); - } - - /** - * Cancels a transaction on the {@link ITransactionQueue}. - * - * @param transaction - */ - public void cancelTransaction(@NonNull Transaction transaction) { - getQueue().cancel(transaction); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/ContentResolverNotifier.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/ContentResolverNotifier.java deleted file mode 100644 index fcf0eb1d9..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/ContentResolverNotifier.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.content.ContentResolver; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.SqlUtils; -import com.raizlabs.android.dbflow.sql.language.SQLOperator; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.ModelAdapter; - -/** - * The default use case, it notifies via the {@link ContentResolver} system. - */ -public class ContentResolverNotifier implements ModelNotifier { - - @NonNull - private final String contentAuthority; - - public ContentResolverNotifier(@NonNull String contentAuthority) { - this.contentAuthority = contentAuthority; - } - - @Override - public void notifyModelChanged(@NonNull T model, @NonNull ModelAdapter adapter, - @NonNull BaseModel.Action action) { - if (FlowContentObserver.shouldNotify()) { - FlowManager.getContext().getContentResolver() - .notifyChange(SqlUtils.getNotificationUri(contentAuthority, - adapter.getModelClass(), action, - adapter.getPrimaryConditionClause(model).getConditions()), null, true); - } - } - - @Override - public void notifyTableChanged(@NonNull Class table, @NonNull BaseModel.Action action) { - if (FlowContentObserver.shouldNotify()) { - FlowManager.getContext().getContentResolver() - .notifyChange(SqlUtils.getNotificationUri(contentAuthority, - table, action, (SQLOperator[]) null), null, true); - } - } - - @Override - public TableNotifierRegister newRegister() { - return new FlowContentTableNotifierRegister(contentAuthority); - } - - public static class FlowContentTableNotifierRegister implements TableNotifierRegister { - private final FlowContentObserver flowContentObserver; - - @Nullable - private OnTableChangedListener tableChangedListener; - - public FlowContentTableNotifierRegister(@NonNull String contentAuthority) { - flowContentObserver = new FlowContentObserver(contentAuthority); - flowContentObserver.addOnTableChangedListener(internalContentChangeListener); - } - - @Override - public void register(@NonNull Class tClass) { - flowContentObserver.registerForContentChanges(FlowManager.getContext(), tClass); - } - - @Override - public void unregister(@NonNull Class tClass) { - flowContentObserver.unregisterForContentChanges(FlowManager.getContext()); - } - - @Override - public void unregisterAll() { - flowContentObserver.removeTableChangedListener(internalContentChangeListener); - this.tableChangedListener = null; - } - - @Override - public void setListener(@Nullable OnTableChangedListener contentChangeListener) { - this.tableChangedListener = contentChangeListener; - } - - @Override - public boolean isSubscribed() { - return !flowContentObserver.isSubscribed(); - } - - private final OnTableChangedListener internalContentChangeListener - = new OnTableChangedListener() { - - @Override - public void onTableChanged(@Nullable Class tableChanged, @NonNull BaseModel.Action action) { - if (tableChangedListener != null) { - tableChangedListener.onTableChanged(tableChanged, action); - } - } - }; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/DBBatchSaveQueue.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/DBBatchSaveQueue.java deleted file mode 100644 index db91ff429..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/DBBatchSaveQueue.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.transaction.ProcessModelTransaction; -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * Description: This queue will bulk save items added to it when it gets access to the DB. It should only exist as one entity. - * It will save the {@link #MODEL_SAVE_SIZE} at a time or more only when the limit is reached. It will not - */ -public class DBBatchSaveQueue extends Thread { - - /** - * Once the queue size reaches 50 or larger, the thread will be interrupted and we will batch save the models. - */ - private static final int MODEL_SAVE_SIZE = 50; - - /** - * The default time that it will awake the save queue thread to check if any models are still waiting to be saved - */ - private static final int sMODEL_SAVE_CHECK_TIME = 30000; - - /** - * Tells how many items to save at a time. This can be set using {@link #setModelSaveSize(int)} - */ - private int modelSaveSize = MODEL_SAVE_SIZE; - - /** - * Sets the time we check periodically for leftover DB objects in our queue to save. - */ - private long modelSaveCheckTime = sMODEL_SAVE_CHECK_TIME; - - /** - * The list of DB objects that we will save here - */ - private final ArrayList models; - - /** - * If true, this queue will quit. - */ - private boolean isQuitting = false; - - private Transaction.Error errorListener; - private Transaction.Success successListener; - private Runnable emptyTransactionListener; - - private DatabaseDefinition databaseDefinition; - - /** - * Creates a new instance of this class to batch save DB object classes. - */ - DBBatchSaveQueue(DatabaseDefinition databaseDefinition) { - super("DBBatchSaveQueue"); - this.databaseDefinition = databaseDefinition; - models = new ArrayList<>(); - } - - /** - * Sets how many models to save at a time in this queue. - * Increase it for larger batches, but slower recovery time. - * Smaller the batch, the more time it takes to save overall. - */ - public void setModelSaveSize(int mModelSaveSize) { - this.modelSaveSize = mModelSaveSize; - } - - /** - * Sets how long, in millis that this queue will check for leftover DB objects that have not been saved yet. - * The default is {@link #sMODEL_SAVE_CHECK_TIME} - * - * @param time The time, in millis that queue automatically checks for leftover DB objects in this queue. - */ - public void setModelSaveCheckTime(long time) { - this.modelSaveCheckTime = time; - } - - - /** - * Listener for errors in each batch {@link Transaction}. Called from the DBBatchSaveQueue thread. - * - * @param errorListener The listener to use. - */ - public void setErrorListener(@Nullable Transaction.Error errorListener) { - this.errorListener = errorListener; - } - - /** - * Listener for batch updates. Called from the DBBatchSaveQueue thread. - * - * @param successListener The listener to get notified when changes are successful. - */ - public void setSuccessListener(@Nullable Transaction.Success successListener) { - this.successListener = successListener; - } - - /** - * Listener for when there is no work done. Called from the DBBatchSaveQueue thread. - * - * @param emptyTransactionListener The listener to get notified when the save queue thread ran but was empty. - */ - public void setEmptyTransactionListener(@Nullable Runnable emptyTransactionListener) { - this.emptyTransactionListener = emptyTransactionListener; - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - super.run(); - Looper.prepare(); - android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); - while (true) { - final ArrayList tmpModels; - synchronized (models) { - tmpModels = new ArrayList<>(models); - models.clear(); - } - if (tmpModels.size() > 0) { - databaseDefinition.beginTransactionAsync( - new ProcessModelTransaction.Builder(modelSaver) - .addAll(tmpModels) - .build()) - .success(successCallback) - .error(errorCallback) - .build() - .execute(); - } else if (emptyTransactionListener != null) { - emptyTransactionListener.run(); - } - - try { - //sleep, and then check for leftovers - Thread.sleep(modelSaveCheckTime); - } catch (InterruptedException e) { - FlowLog.log(FlowLog.Level.I, "DBRequestQueue Batch interrupted to start saving"); - } - - if (isQuitting) { - return; - } - } - } - - /** - * Will cause the queue to wake from sleep and handle it's current list of items. - */ - public void purgeQueue() { - interrupt(); - } - - /** - * Adds an object to this queue. - */ - public void add(@NonNull final Object inModel) { - synchronized (models) { - models.add(inModel); - - if (models.size() > modelSaveSize) { - interrupt(); - } - } - } - - /** - * Adds a {@link java.util.Collection} of DB objects to this queue - */ - public void addAll(@NonNull final Collection list) { - synchronized (models) { - models.addAll(list); - - if (models.size() > modelSaveSize) { - interrupt(); - } - } - } - - /** - * Adds a {@link java.util.Collection} of class that extend Object to this queue - */ - public void addAll2(@NonNull final Collection list) { - synchronized (models) { - models.addAll(list); - - if (models.size() > modelSaveSize) { - interrupt(); - } - } - } - - /** - * Removes a DB object from this queue before it is processed. - */ - public void remove(@NonNull final Object outModel) { - synchronized (models) { - models.remove(outModel); - } - } - - /** - * Removes a {@link java.util.Collection} of DB object from this queue - * before it is processed. - */ - public void removeAll(@NonNull final Collection outCollection) { - synchronized (models) { - models.removeAll(outCollection); - } - } - - /** - * Removes a {@link java.util.Collection} of DB objects from this queue - * before it is processed. - */ - public void removeAll2(@NonNull final Collection outCollection) { - synchronized (models) { - models.removeAll(outCollection); - } - } - - /** - * Quits this queue after it sleeps for the {@link #modelSaveCheckTime} - */ - public void quit() { - isQuitting = true; - } - - private final ProcessModelTransaction.ProcessModel modelSaver = new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(Object model, DatabaseWrapper wrapper) { - if (model instanceof Model) { - ((Model) model).save(); - } else if (model != null) { - Class modelClass = model.getClass(); - //noinspection unchecked - FlowManager.getModelAdapter(modelClass).save(model); - } - } - }; - - private final Transaction.Success successCallback = new Transaction.Success() { - @Override - public void onSuccess(@NonNull Transaction transaction) { - if (successListener != null) { - successListener.onSuccess(transaction); - } - } - }; - - private final Transaction.Error errorCallback = new Transaction.Error() { - @Override - public void onError(@NonNull Transaction transaction, @NonNull Throwable error) { - if (errorListener != null) { - errorListener.onError(transaction, error); - } - } - }; - -} - diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/DirectModelNotifier.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/DirectModelNotifier.java deleted file mode 100644 index d82ac32ab..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/DirectModelNotifier.java +++ /dev/null @@ -1,187 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseConfig; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.ModelAdapter; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Description: Directly notifies about model changes. Users should use {@link #get()} to use the shared - * instance in {@link DatabaseConfig.Builder} - */ -@SuppressWarnings("unchecked") -public class DirectModelNotifier implements ModelNotifier { - - private static DirectModelNotifier notifier; - - @NonNull - public static DirectModelNotifier get() { - if (notifier == null) { - notifier = new DirectModelNotifier(); - } - return notifier; - } - - public interface OnModelStateChangedListener { - - void onModelChanged(@NonNull T model, @NonNull BaseModel.Action action); - - } - - public interface ModelChangedListener extends OnModelStateChangedListener, OnTableChangedListener { - - } - - private final Map, Set> modelChangedListenerMap = new LinkedHashMap<>(); - - private final Map, Set> tableChangedListenerMap = new LinkedHashMap<>(); - - - private final TableNotifierRegister singleRegister = new DirectTableNotifierRegister(); - - /** - * Private constructor. Use shared {@link #get()} to ensure singular instance. - */ - private DirectModelNotifier() { - if (notifier != null) { - throw new IllegalStateException("Cannot instantiate more than one DirectNotifier. Use DirectNotifier.get()"); - } - } - - @Override - public void notifyModelChanged(@NonNull T model, @NonNull ModelAdapter adapter, - @NonNull BaseModel.Action action) { - final Set listeners = modelChangedListenerMap.get(adapter.getModelClass()); - if (listeners != null) { - for (OnModelStateChangedListener listener : listeners) { - if (listener != null) { - listener.onModelChanged(model, action); - } - } - } - } - - @Override - public void notifyTableChanged(@NonNull Class table, @NonNull BaseModel.Action action) { - final Set listeners = tableChangedListenerMap.get(table); - if (listeners != null) { - for (OnTableChangedListener listener : listeners) { - if (listener != null) { - listener.onTableChanged(table, action); - } - } - } - } - - @Override - public TableNotifierRegister newRegister() { - return singleRegister; - } - - public void registerForModelChanges(@NonNull Class table, - @NonNull ModelChangedListener listener) { - registerForModelStateChanges(table, listener); - registerForTableChanges(table, listener); - } - - public void registerForModelStateChanges(@NonNull Class table, - @NonNull OnModelStateChangedListener listener) { - Set listeners = modelChangedListenerMap.get(table); - if (listeners == null) { - listeners = new LinkedHashSet<>(); - modelChangedListenerMap.put(table, listeners); - } - listeners.add(listener); - } - - public void registerForTableChanges(@NonNull Class table, - @NonNull OnTableChangedListener listener) { - Set listeners = tableChangedListenerMap.get(table); - if (listeners == null) { - listeners = new LinkedHashSet<>(); - tableChangedListenerMap.put(table, listeners); - } - listeners.add(listener); - } - - public void unregisterForModelChanges(@NonNull Class table, - @NonNull ModelChangedListener listener) { - unregisterForModelStateChanges(table, listener); - unregisterForTableChanges(table, listener); - } - - - public void unregisterForModelStateChanges(@NonNull Class table, - @NonNull OnModelStateChangedListener listener) { - Set listeners = modelChangedListenerMap.get(table); - if (listeners != null) { - listeners.remove(listener); - } - } - - public void unregisterForTableChanges(@NonNull Class table, - @NonNull OnTableChangedListener listener) { - Set listeners = tableChangedListenerMap.get(table); - if (listeners != null) { - listeners.remove(listener); - } - } - - private class DirectTableNotifierRegister implements TableNotifierRegister { - private List registeredTables = new ArrayList<>(); - - @Nullable - private OnTableChangedListener modelChangedListener; - - @Override - public void register(@NonNull Class tClass) { - registeredTables.add(tClass); - registerForTableChanges(tClass, internalChangeListener); - } - - @Override - public void unregister(@NonNull Class tClass) { - registeredTables.remove(tClass); - unregisterForTableChanges(tClass, internalChangeListener); - } - - @Override - public void unregisterAll() { - for (Class table : registeredTables) { - unregisterForTableChanges(table, internalChangeListener); - } - this.modelChangedListener = null; - } - - @Override - public void setListener(@Nullable OnTableChangedListener modelChangedListener) { - this.modelChangedListener = modelChangedListener; - } - - @Override - public boolean isSubscribed() { - return !registeredTables.isEmpty(); - } - - private final OnTableChangedListener internalChangeListener - = new OnTableChangedListener() { - - @Override - public void onTableChanged(@Nullable Class table, @NonNull BaseModel.Action action) { - if (modelChangedListener != null) { - modelChangedListener.onTableChanged(table, action); - } - } - }; - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java deleted file mode 100644 index 32a667779..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/FlowContentObserver.java +++ /dev/null @@ -1,310 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.annotation.TargetApi; -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Build; -import android.os.Build.VERSION_CODES; -import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseConfig; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.SqlUtils; -import com.raizlabs.android.dbflow.sql.language.NameAlias; -import com.raizlabs.android.dbflow.sql.language.Operator; -import com.raizlabs.android.dbflow.sql.language.SQLOperator; -import com.raizlabs.android.dbflow.structure.BaseModel.Action; -import com.raizlabs.android.dbflow.structure.Model; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Description: Listens for {@link Model} changes. Register for specific - * tables with {@link #addModelChangeListener(FlowContentObserver.OnModelStateChangedListener)}. - * Provides ability to register and deregister listeners for when data is inserted, deleted, updated, and saved if the device is - * above {@link VERSION_CODES#JELLY_BEAN}. If below it will only provide one callback. This is to be paired - * with the {@link ContentResolverNotifier} specified in the {@link DatabaseConfig} by default. - */ -public class FlowContentObserver extends ContentObserver { - - private static final AtomicInteger REGISTERED_COUNT = new AtomicInteger(0); - private static boolean forceNotify = false; - @NonNull private final String contentAuthority; - - /** - * @return true if we have registered for content changes. Otherwise we do not notify - * in {@link SqlUtils} - * for efficiency purposes. - */ - public static boolean shouldNotify() { - return forceNotify || REGISTERED_COUNT.get() > 0; - } - - /** - * Removes count of observers registered, so we do not send out calls when {@link Model} changes. - */ - public static void clearRegisteredObserverCount() { - REGISTERED_COUNT.set(0); - } - - /** - * @param forceNotify if true, this will force itself to notify whenever a model changes even though - * an observer (appears to be) is not registered. - */ - public static void setShouldForceNotify(boolean forceNotify) { - FlowContentObserver.forceNotify = forceNotify; - } - - /** - * Listens for specific model changes. This is only available in {@link VERSION_CODES#JELLY_BEAN} - * or higher due to the api of {@link ContentObserver}. - */ - public interface OnModelStateChangedListener { - - /** - * Notifies that the state of a {@link Model} - * has changed for the table this is registered for. - * - * @param table The table that this change occurred on. This is ONLY available on {@link VERSION_CODES#JELLY_BEAN} - * and up. - * @param action The action on the model. for versions prior to {@link VERSION_CODES#JELLY_BEAN} , - * the {@link Action#CHANGE} will always be called for any action. - * @param primaryKeyValues The array of primary {@link SQLOperator} of what changed. Call {@link SQLOperator#columnName()} - * and {@link SQLOperator#value()} to get each information. - */ - void onModelStateChanged(@Nullable Class table, Action action, @NonNull SQLOperator[] primaryKeyValues); - } - - public interface ContentChangeListener extends OnModelStateChangedListener, OnTableChangedListener { - - } - - private final Set modelChangeListeners = new CopyOnWriteArraySet<>(); - private final Set onTableChangedListeners = new CopyOnWriteArraySet<>(); - private final Map> registeredTables = new HashMap<>(); - private final Set notificationUris = new HashSet<>(); - private final Set tableUris = new HashSet<>(); - - protected boolean isInTransaction = false; - private boolean notifyAllUris = false; - - public FlowContentObserver(@NonNull String contentAuthority) { - super(null); - this.contentAuthority = contentAuthority; - } - - public FlowContentObserver(@Nullable Handler handler, @NonNull String contentAuthority) { - super(handler); - this.contentAuthority = contentAuthority; - } - - /** - * If true, this class will get specific when it needs to, such as using all {@link Action} qualifiers. - * If false, it only uses the {@link Action#CHANGE} action in callbacks. - * - * @param notifyAllUris - */ - public void setNotifyAllUris(boolean notifyAllUris) { - this.notifyAllUris = notifyAllUris; - } - - /** - * Starts a transaction where when it is finished, this class will receive a notification of all of the changes by - * calling {@link #endTransactionAndNotify()}. Note it may lead to unexpected behavior if called from different threads. - */ - public void beginTransaction() { - if (!isInTransaction) { - isInTransaction = true; - } - } - - /** - * Ends the transaction where it finishes, and will call {@link #onChange(boolean, Uri)} for Jelly Bean and up for - * every URI called (if set), or {@link #onChange(boolean)} once for lower than Jelly bean. - */ - public void endTransactionAndNotify() { - if (isInTransaction) { - isInTransaction = false; - - if (Build.VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN) { - onChange(true); - } else { - synchronized (notificationUris) { - for (Uri uri : notificationUris) { - onChange(true, uri, true); - } - notificationUris.clear(); - } - synchronized (tableUris) { - for (Uri uri : tableUris) { - for (OnTableChangedListener onTableChangedListener : onTableChangedListeners) { - onTableChangedListener.onTableChanged(registeredTables.get(uri.getAuthority()), - Action.valueOf(uri.getFragment())); - } - } - tableUris.clear(); - } - } - } - } - - /** - * Add a listener for model changes - * - * @param modelChangeListener Generic model change events from an {@link Action} - */ - public void addModelChangeListener(@NonNull OnModelStateChangedListener modelChangeListener) { - modelChangeListeners.add(modelChangeListener); - } - - /** - * Removes a listener for model changes - * - * @param modelChangeListener Generic model change events from a {@link Action} - */ - public void removeModelChangeListener(@NonNull OnModelStateChangedListener modelChangeListener) { - modelChangeListeners.remove(modelChangeListener); - } - - public void addOnTableChangedListener(@NonNull OnTableChangedListener onTableChangedListener) { - onTableChangedListeners.add(onTableChangedListener); - } - - public void removeTableChangedListener(@NonNull OnTableChangedListener onTableChangedListener) { - onTableChangedListeners.remove(onTableChangedListener); - } - - /** - * Add a listener for model + table changes - * - * @param contentChangeListener Generic model change events from an {@link Action} - */ - public void addContentChangeListener(@NonNull ContentChangeListener contentChangeListener) { - modelChangeListeners.add(contentChangeListener); - onTableChangedListeners.add(contentChangeListener); - } - - /** - * Removes a listener for model + table changes - * - * @param contentChangeListener Generic model change events from a {@link Action} - */ - public void removeContentChangeListener(@NonNull ContentChangeListener contentChangeListener) { - modelChangeListeners.remove(contentChangeListener); - onTableChangedListeners.remove(contentChangeListener); - } - - /** - * Registers the observer for model change events for specific class. - */ - public void registerForContentChanges(@NonNull Context context, - @NonNull Class table) { - registerForContentChanges(context.getContentResolver(), table); - } - - /** - * Registers the observer for model change events for specific class. - */ - public void registerForContentChanges(@NonNull ContentResolver contentResolver, - @NonNull Class table) { - contentResolver.registerContentObserver( - SqlUtils.getNotificationUri(contentAuthority, table, null), true, this); - REGISTERED_COUNT.incrementAndGet(); - if (!registeredTables.containsValue(table)) { - registeredTables.put(FlowManager.getTableName(table), table); - } - } - - /** - * Unregisters this list for model change events - */ - public void unregisterForContentChanges(@NonNull Context context) { - context.getContentResolver().unregisterContentObserver(this); - REGISTERED_COUNT.decrementAndGet(); - registeredTables.clear(); - } - - public boolean isSubscribed() { - return !registeredTables.isEmpty(); - } - - @Override - public void onChange(boolean selfChange) { - for (OnModelStateChangedListener modelChangeListener : modelChangeListeners) { - modelChangeListener.onModelStateChanged(null, Action.CHANGE, new SQLOperator[0]); - } - - for (OnTableChangedListener onTableChangedListener : onTableChangedListeners) { - onTableChangedListener.onTableChanged(null, Action.CHANGE); - } - } - - @TargetApi(VERSION_CODES.JELLY_BEAN) - @Override - public void onChange(boolean selfChange, Uri uri) { - onChange(selfChange, uri, false); - } - - @TargetApi(VERSION_CODES.JELLY_BEAN) - private void onChange(boolean selfChanges, Uri uri, boolean calledInternally) { - String fragment = uri.getFragment(); - String tableName = uri.getQueryParameter(SqlUtils.TABLE_QUERY_PARAM); - - String columnName; - String param; - - Set queryNames = uri.getQueryParameterNames(); - SQLOperator[] columnsChanged = new SQLOperator[queryNames.size() - 1]; - if (!queryNames.isEmpty()) { - int index = 0; - for (String key : queryNames) { - if (!SqlUtils.TABLE_QUERY_PARAM.equals(key)) { - param = Uri.decode(uri.getQueryParameter(key)); - columnName = Uri.decode(key); - columnsChanged[index] = Operator.op(new NameAlias.Builder(columnName).build()) - .eq(param); - index++; - } - } - } - - Class table = registeredTables.get(tableName); - Action action = Action.valueOf(fragment); - if (!isInTransaction) { - - for (OnModelStateChangedListener modelChangeListener : modelChangeListeners) { - modelChangeListener.onModelStateChanged(table, action, columnsChanged); - } - - if (!calledInternally) { - for (OnTableChangedListener onTableChangeListener : onTableChangedListeners) { - onTableChangeListener.onTableChanged(table, action); - } - } - } else { - // convert this uri to a CHANGE op if we don't care about individual changes. - if (!notifyAllUris) { - action = Action.CHANGE; - uri = SqlUtils.getNotificationUri(contentAuthority, table, action); - } - synchronized (notificationUris) { - // add and keep track of unique notification uris for when transaction completes. - notificationUris.add(uri); - } - - synchronized (tableUris) { - tableUris.add(SqlUtils.getNotificationUri(contentAuthority, table, action)); - } - } - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/ModelNotifier.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/ModelNotifier.java deleted file mode 100644 index 279df9926..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/ModelNotifier.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.ModelAdapter; - -/** - * Interface for defining how we notify model changes. - */ -public interface ModelNotifier { - - void notifyModelChanged(@NonNull T model, @NonNull ModelAdapter adapter, - @NonNull BaseModel.Action action); - - void notifyTableChanged(@NonNull Class table, @NonNull BaseModel.Action action); - - TableNotifierRegister newRegister(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/NotifyDistributor.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/NotifyDistributor.java deleted file mode 100644 index af8279455..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/NotifyDistributor.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.ModelAdapter; - -/** - * Description: Distributes notifications to the {@link ModelNotifier}. - */ -public class NotifyDistributor implements ModelNotifier { - - private static NotifyDistributor distributor; - - @NonNull - public static NotifyDistributor get() { - if (distributor == null) { - distributor = new NotifyDistributor(); - } - return distributor; - } - - @Override - public TableNotifierRegister newRegister() { - throw new RuntimeException("Cannot create a register from the distributor class"); - } - - @Override - public void notifyModelChanged(@NonNull TModel model, - @NonNull ModelAdapter adapter, - @NonNull BaseModel.Action action) { - FlowManager.getModelNotifierForTable(adapter.getModelClass()) - .notifyModelChanged(model, adapter, action); - } - - /** - * Notifies listeners of table-level changes from the SQLite-wrapper language. - */ - @Override - public void notifyTableChanged(@NonNull Class table, - @NonNull BaseModel.Action action) { - FlowManager.getModelNotifierForTable(table).notifyTableChanged(table, action); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/OnTableChangedListener.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/OnTableChangedListener.java deleted file mode 100644 index bc0285541..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/OnTableChangedListener.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.structure.BaseModel; - -/** - * Interface for when a generic change on a table occurs. - */ -public interface OnTableChangedListener { - - /** - * Called when table changes. - * - * @param tableChanged The table that has changed. NULL unless version of app is {@link Build.VERSION_CODES#JELLY_BEAN} - * or higher. - * @param action The action that occurred. - */ - void onTableChanged(@Nullable Class tableChanged, @NonNull BaseModel.Action action); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/StubContentProvider.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/StubContentProvider.java deleted file mode 100644 index fd498ca43..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/StubContentProvider.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -/** - * Description: - */ - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Description: Used as a stub, include this in order to work around Android O changes to {@link ContentProvider} - */ -public class StubContentProvider extends ContentProvider { - - @Nullable - @Override - public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { - return null; - } - - @Nullable - @Override - public Cursor query(@NonNull Uri uri, - @Nullable String[] projection, - @Nullable String selection, - @Nullable String[] selectionArgs, - @Nullable String sortOrder) { - return null; - } - - @Override - public boolean onCreate() { - return true; - } - - @Override - public int update(@NonNull Uri uri, @Nullable ContentValues values, - @Nullable String selection, @Nullable String[] selectionArgs) { - return 0; - } - - @Override - public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { - return 0; - } - - @Nullable - @Override - public String getType(@NonNull Uri uri) { - return null; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/TableNotifierRegister.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/TableNotifierRegister.java deleted file mode 100644 index 6ff597db6..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/runtime/TableNotifierRegister.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.runtime; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Description: Defines how {@link ModelNotifier} registers listeners. Abstracts that away. - */ -public interface TableNotifierRegister { - - void register(@NonNull Class tClass); - - void unregister(@NonNull Class tClass); - - void unregisterAll(); - - void setListener(@Nullable OnTableChangedListener listener); - - boolean isSubscribed(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/BaseAsyncObject.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/BaseAsyncObject.java deleted file mode 100644 index 310012975..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/BaseAsyncObject.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.raizlabs.android.dbflow.sql; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.database.transaction.ITransaction; -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction; - -/** - * Description: Internal use to provide common implementation for async objects. - */ -public class BaseAsyncObject { - - private Transaction.Success successCallback; - private Transaction.Error errorCallback; - private Transaction currentTransaction; - - private final Class table; - private final DatabaseDefinition databaseDefinition; - - public BaseAsyncObject(@NonNull Class table) { - this.table = table; - databaseDefinition = FlowManager.getDatabaseForTable(table); - } - - @NonNull - public Class getTable() { - return table; - } - - /** - * Listen for any errors that occur during operations on this {@link TAsync}. - */ - @SuppressWarnings("unchecked") - public TAsync error(@Nullable Transaction.Error errorCallback) { - this.errorCallback = errorCallback; - return (TAsync) this; - } - - /** - * Listens for successes on this {@link TAsync}. Will return the {@link Transaction}. - */ - @SuppressWarnings("unchecked") - public TAsync success(@Nullable Transaction.Success success) { - this.successCallback = success; - return (TAsync) this; - } - - /** - * Cancels current running transaction. - */ - public void cancel() { - if (currentTransaction != null) { - currentTransaction.cancel(); - } - } - - protected void executeTransaction(@NonNull ITransaction transaction) { - cancel(); - currentTransaction = databaseDefinition - .beginTransactionAsync(transaction) - .error(error) - .success(success) - .build(); - currentTransaction.execute(); - } - - protected void onError(@NonNull Transaction transaction, Throwable error) { - - } - - protected void onSuccess(@NonNull Transaction transaction) { - - } - - private final Transaction.Error error = new Transaction.Error() { - @Override - public void onError(@NonNull Transaction transaction, @NonNull Throwable error) { - if (errorCallback != null) { - errorCallback.onError(transaction, error); - } - BaseAsyncObject.this.onError(transaction, error); - currentTransaction = null; - } - }; - - private final Transaction.Success success = new Transaction.Success() { - @Override - public void onSuccess(@NonNull Transaction transaction) { - if (successCallback != null) { - successCallback.onSuccess(transaction); - } - BaseAsyncObject.this.onSuccess(transaction); - currentTransaction = null; - } - }; -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java deleted file mode 100644 index 0916a74a6..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/SqlUtils.java +++ /dev/null @@ -1,275 +0,0 @@ -package com.raizlabs.android.dbflow.sql; - -import android.content.ContentValues; -import android.database.ContentObserver; -import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.StringUtils; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.runtime.NotifyDistributor; -import com.raizlabs.android.dbflow.sql.language.NameAlias; -import com.raizlabs.android.dbflow.sql.language.Operator; -import com.raizlabs.android.dbflow.sql.language.OperatorGroup; -import com.raizlabs.android.dbflow.sql.language.SQLOperator; -import com.raizlabs.android.dbflow.structure.BaseModel.Action; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.Map; - -/** - * Description: Provides some handy methods for dealing with SQL statements. It's purpose is to move the - * methods away from the {@link Model} class and let any class use these. - */ -public class SqlUtils { - - public static final String TABLE_QUERY_PARAM = "tableName"; - - private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); - - /** - * Notifies the {@link ContentObserver} that the model has changed. - */ - @Deprecated - public static void notifyModelChanged(@NonNull String contentAuthority, - Class table, Action action, - Iterable sqlOperators) { - FlowManager.getContext().getContentResolver().notifyChange( - getNotificationUri(contentAuthority, table, action, sqlOperators), null, true); - } - - /** - * Performs necessary logic to notify of {@link Model} changes. - * - * @see NotifyDistributor - */ - @SuppressWarnings("unchecked") - @Deprecated - public static void notifyModelChanged(@Nullable TModel model, - @NonNull ModelAdapter modelAdapter, - @NonNull Action action) { - NotifyDistributor.get().notifyModelChanged(model, modelAdapter, action); - } - - /** - * Notifies listeners of table-level changes from the SQLite-wrapper language. - * - * @see NotifyDistributor - */ - @Deprecated - public static void notifyTableChanged(@NonNull Class table, - @NonNull Action action) { - NotifyDistributor.get().notifyTableChanged(table, action); - } - - /** - * Constructs a {@link Uri} from a set of {@link SQLOperator} for specific table. - * - * @param modelClass The class of table, - * @param action The action to use. - * @param conditions The set of key-value {@link SQLOperator} to construct into a uri. - * @return The {@link Uri}. - */ - public static Uri getNotificationUri(@NonNull String contentAuthority, - @NonNull Class modelClass, - @Nullable Action action, - @Nullable Iterable conditions) { - Uri.Builder uriBuilder = new Uri.Builder().scheme("dbflow") - .authority(contentAuthority) - .appendQueryParameter(TABLE_QUERY_PARAM, FlowManager.getTableName(modelClass)); - if (action != null) { - uriBuilder.fragment(action.name()); - } - if (conditions != null) { - for (SQLOperator condition : conditions) { - uriBuilder.appendQueryParameter(Uri.encode(condition.columnName()), Uri.encode(String.valueOf(condition.value()))); - } - } - return uriBuilder.build(); - } - - - /** - * Constructs a {@link Uri} from a set of {@link SQLOperator} for specific table. - * - * @param modelClass The class of table, - * @param action The action to use. - * @param conditions The set of key-value {@link SQLOperator} to construct into a uri. - * @return The {@link Uri}. - */ - public static Uri getNotificationUri(@NonNull String contentAuthority, - @NonNull Class modelClass, - @NonNull Action action, - @Nullable SQLOperator[] conditions) { - Uri.Builder uriBuilder = new Uri.Builder().scheme("dbflow") - .authority(contentAuthority) - .appendQueryParameter(TABLE_QUERY_PARAM, FlowManager.getTableName(modelClass)); - if (action != null) { - uriBuilder.fragment(action.name()); - } - if (conditions != null && conditions.length > 0) { - for (SQLOperator condition : conditions) { - if (condition != null) { - uriBuilder.appendQueryParameter(Uri.encode(condition.columnName()), Uri.encode(String.valueOf(condition.value()))); - } - } - } - return uriBuilder.build(); - } - - /** - * Returns the uri for notifications from model changes - * - * @param modelClass The class to get table from. - * @param action the action changed. - * @param notifyKey The column key. - * @param notifyValue The column value that gets turned into a String. - * @return Notification uri. - */ - - public static Uri getNotificationUri(@NonNull String contentAuthority, - @NonNull Class modelClass, - @NonNull Action action, - @NonNull String notifyKey, - @Nullable Object notifyValue) { - Operator operator = null; - if (StringUtils.isNotNullOrEmpty(notifyKey)) { - operator = Operator.op(new NameAlias.Builder(notifyKey).build()).value(notifyValue); - } - return getNotificationUri(contentAuthority, modelClass, action, new SQLOperator[]{operator}); - } - - /** - * @param modelClass The model class to use. - * @param action The {@link Action} to use. - * @return The uri for updates to {@link Model}, meant for general changes. - */ - public static Uri getNotificationUri(@NonNull String contentAuthority, - @NonNull Class modelClass, - @NonNull Action action) { - return getNotificationUri(contentAuthority, modelClass, action, "", null); - } - - - /** - * Drops an active TRIGGER by specifying the onTable and triggerName - * - * @param onTable The table that this trigger runs on - * @param triggerName The name of the trigger - */ - public static void dropTrigger(Class onTable, String triggerName) { - QueryBuilder queryBuilder = new QueryBuilder("DROP TRIGGER IF EXISTS ") - .append(triggerName); - FlowManager.getDatabaseForTable(onTable).getWritableDatabase().execSQL(queryBuilder.getQuery()); - } - - /** - * Drops an active TRIGGER by specifying the databaseWrapper and triggerName - * - * @param databaseWrapper The manually specified wrapper. - * @param triggerName The name of the trigger - */ - public static void dropTrigger(DatabaseWrapper databaseWrapper, String triggerName) { - QueryBuilder queryBuilder = new QueryBuilder("DROP TRIGGER IF EXISTS ") - .append(triggerName); - databaseWrapper.execSQL(queryBuilder.getQuery()); - } - - /** - * Drops an active INDEX by specifying the databaseWrapper and indexName - * - * @param databaseWrapper The manually specified wrapper. - * @param indexName The name of the index. - */ - public static void dropIndex(@NonNull DatabaseWrapper databaseWrapper, - @NonNull String indexName) { - QueryBuilder queryBuilder = new QueryBuilder("DROP INDEX IF EXISTS ") - .append(QueryBuilder.quoteIfNeeded(indexName)); - databaseWrapper.execSQL(queryBuilder.getQuery()); - } - - /** - * Drops an active INDEX by specifying the onTable and indexName - * - * @param onTable The table that this trigger runs on - * @param indexName The name of the index. - */ - public static void dropIndex(Class onTable, String indexName) { - dropIndex(FlowManager.getDatabaseForTable(onTable).getWritableDatabase(), indexName); - } - - /** - * Adds {@link ContentValues} to the specified {@link OperatorGroup}. - * - * @param contentValues The content values to convert. - * @param operatorGroup The group to put them into as {@link Operator}. - */ - public static void addContentValues(@NonNull ContentValues contentValues, @NonNull OperatorGroup operatorGroup) { - java.util.Set> entries = contentValues.valueSet(); - - for (Map.Entry entry : entries) { - String key = entry.getKey(); - operatorGroup.and(Operator.op(new NameAlias.Builder(key).build()) - .is(contentValues.get(key))); - } - } - - /** - * @param contentValues The object to check existence of. - * @param key The key to check. - * @return The key, whether it's quoted or not. - */ - @NonNull - public static String getContentValuesKey(ContentValues contentValues, String key) { - String quoted = QueryBuilder.quoteIfNeeded(key); - if (contentValues.containsKey(quoted)) { - return quoted; - } else { - String stripped = QueryBuilder.stripQuotes(key); - if (contentValues.containsKey(stripped)) { - return stripped; - } else { - throw new IllegalArgumentException("Could not find the specified key in the Content Values object."); - } - } - } - - public static long longForQuery(@NonNull DatabaseWrapper wrapper, - @NonNull String query) { - DatabaseStatement statement = wrapper.compileStatement(query); - try { - return statement.simpleQueryForLong(); - } finally { - statement.close(); - } - } - - public static double doubleForQuery(@NonNull DatabaseWrapper wrapper, - @NonNull String query) { - DatabaseStatement statement = wrapper.compileStatement(query); - try { - return statement.simpleQueryForLong(); - } finally { - statement.close(); - } - } - - /** - * Converts a byte[] to a String hex representation for within wrapper queries. - */ - @NonNull - public static String byteArrayToHexString(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); - } -} - diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Actionable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Actionable.java deleted file mode 100644 index f461f91d8..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Actionable.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.BaseModel.Action; - -/** - * Description: Provides {@link Action} for SQL constructs. - */ -public interface Actionable { - - @NonNull - Action getPrimaryAction(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseModelQueriable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseModelQueriable.java deleted file mode 100644 index bb7651f9a..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseModelQueriable.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.list.FlowCursorList; -import com.raizlabs.android.dbflow.list.FlowQueryList; -import com.raizlabs.android.dbflow.runtime.NotifyDistributor; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.queriable.AsyncQuery; -import com.raizlabs.android.dbflow.sql.queriable.ListModelLoader; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.sql.queriable.SingleModelLoader; -import com.raizlabs.android.dbflow.structure.InstanceAdapter; -import com.raizlabs.android.dbflow.structure.QueryModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.List; - -/** - * Description: Provides a base implementation of {@link ModelQueriable} to simplify a lot of code. It provides the - * default implementation for convenience. - */ -public abstract class BaseModelQueriable extends BaseQueriable - implements ModelQueriable, Query { - - private InstanceAdapter retrievalAdapter; - - private boolean cachingEnabled = true; - - /** - * Constructs new instance of this class and is meant for subclasses only. - * - * @param table the table that belongs to this query. - */ - protected BaseModelQueriable(Class table) { - super(table); - } - - private InstanceAdapter getRetrievalAdapter() { - if (retrievalAdapter == null) { - retrievalAdapter = FlowManager.getInstanceAdapter(getTable()); - } - return retrievalAdapter; - } - - @NonNull - @Override - public CursorResult queryResults() { - return new CursorResult<>(getRetrievalAdapter().getModelClass(), query()); - } - - @NonNull - @Override - public List queryList() { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Executing query: " + query); - return getListModelLoader().load(query); - } - - @Nullable - @Override - public TModel querySingle() { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Executing query: " + query); - return getSingleModelLoader().load(query); - } - - @Override - public TModel querySingle(@NonNull DatabaseWrapper wrapper) { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Executing query: " + query); - return getSingleModelLoader().load(wrapper, query); - } - - @NonNull - @Override - public List queryList(@NonNull DatabaseWrapper wrapper) { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Executing query: " + query); - return getListModelLoader().load(wrapper, query); - } - - @NonNull - @Override - public FlowCursorList cursorList() { - return new FlowCursorList.Builder<>(getTable()) - .cacheModels(cachingEnabled) - .modelQueriable(this).build(); - } - - @NonNull - @Override - public FlowQueryList flowQueryList() { - return new FlowQueryList.Builder<>(getTable()) - .cacheModels(cachingEnabled) - .modelQueriable(this) - .build(); - } - - @Override - public long executeUpdateDelete() { - return executeUpdateDelete(FlowManager.getWritableDatabaseForTable(getTable())); - } - - @Override - public long executeUpdateDelete(@NonNull DatabaseWrapper databaseWrapper) { - final DatabaseStatement statement = databaseWrapper.compileStatement(getQuery()); - long affected; - try { - affected = statement.executeUpdateDelete(); - - // only notify for affected. - if (affected > 0) { - NotifyDistributor.get().notifyTableChanged(getTable(), getPrimaryAction()); - } - } finally { - statement.close(); - } - return affected; - } - - @NonNull - @Override - public AsyncQuery async() { - return new AsyncQuery<>(this); - } - - @NonNull - @Override - public List queryCustomList(@NonNull Class queryModelClass) { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Executing query: " + query); - QueryModelAdapter adapter = FlowManager.getQueryModelAdapter(queryModelClass); - return cachingEnabled - ? adapter.getListModelLoader().load(query) - : adapter.getNonCacheableListModelLoader().load(query); - } - - @Nullable - @Override - public QueryClass queryCustomSingle(@NonNull Class queryModelClass) { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Executing query: " + query); - QueryModelAdapter adapter = FlowManager.getQueryModelAdapter(queryModelClass); - return cachingEnabled - ? adapter.getSingleModelLoader().load(query) - : adapter.getNonCacheableSingleModelLoader().load(query); - } - - @NonNull - @Override - public ModelQueriable disableCaching() { - cachingEnabled = false; - return this; - } - - private ListModelLoader getListModelLoader() { - return cachingEnabled - ? getRetrievalAdapter().getListModelLoader() - : getRetrievalAdapter().getNonCacheableListModelLoader(); - } - - private SingleModelLoader getSingleModelLoader() { - return cachingEnabled - ? getRetrievalAdapter().getSingleModelLoader() - : getRetrievalAdapter().getNonCacheableSingleModelLoader(); - } - - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseOperator.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseOperator.java deleted file mode 100644 index 5b7535e63..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseOperator.java +++ /dev/null @@ -1,269 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.database.DatabaseUtils; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.converter.TypeConverter; -import com.raizlabs.android.dbflow.data.Blob; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.SqlUtils; - -/** - * Description: Base class for all kinds of {@link SQLOperator} - */ -public abstract class BaseOperator implements SQLOperator { - - @Nullable - public static String convertValueToString(Object value, boolean appendInnerQueryParenthesis) { - return convertValueToString(value, appendInnerQueryParenthesis, true); - } - - /** - * Converts a value input into a String representation of that. - *

- * If it has a {@link TypeConverter}, it first will convert it's value into its {@link TypeConverter#getDBValue(Object)}. - *

- * If the value is a {@link Number}, we return a string rep of that. - *

- * If the value is a {@link BaseModelQueriable} and appendInnerQueryParenthesis is true, - * we return the query wrapped in "()" - *

- * If the value is a {@link NameAlias}, we return the {@link NameAlias#getQuery()} - *

- * If the value is a {@link SQLOperator}, we {@link SQLOperator#appendConditionToQuery(QueryBuilder)}. - *

- * If the value is a {@link Query}, we simply call {@link Query#getQuery()}. - *

- * If the value if a {@link Blob} or byte[] - * - * @param value The value of the column in Model format. - * @param appendInnerQueryParenthesis if its a {@link BaseModelQueriable} and an inner query value - * in a condition, we append parenthesis to the query. - * @return Returns the result as a string that's safe for SQLite. - */ - @SuppressWarnings("unchecked") - @Nullable - public static String convertValueToString(@Nullable Object value, - boolean appendInnerQueryParenthesis, - boolean typeConvert) { - if (value == null) { - return "NULL"; - } else { - String stringVal; - if (typeConvert) { - TypeConverter typeConverter = FlowManager.getTypeConverterForClass(value.getClass()); - if (typeConverter != null) { - value = typeConverter.getDBValue(value); - } - } - - if (value instanceof Number) { - stringVal = String.valueOf(value); - } else if (value instanceof Enum) { - stringVal = DatabaseUtils.sqlEscapeString(((Enum) value).name()); - } else { - if (appendInnerQueryParenthesis && value instanceof BaseModelQueriable) { - stringVal = String.format("(%1s)", ((BaseModelQueriable) value).getQuery().trim()); - } else if (value instanceof NameAlias) { - stringVal = ((NameAlias) value).getQuery(); - } else if (value instanceof SQLOperator) { - QueryBuilder queryBuilder = new QueryBuilder(); - ((SQLOperator) value).appendConditionToQuery(queryBuilder); - stringVal = queryBuilder.toString(); - } else if (value instanceof Query) { - stringVal = ((Query) value).getQuery(); - } else if (value instanceof Blob || value instanceof byte[]) { - byte[] bytes; - if (value instanceof Blob) { - bytes = ((Blob) value).getBlob(); - } else { - bytes = ((byte[]) value); - } - stringVal = "X" + DatabaseUtils.sqlEscapeString(SqlUtils.byteArrayToHexString(bytes)); - } else { - stringVal = String.valueOf(value); - if (!stringVal.equals(Operator.Operation.EMPTY_PARAM)) { - stringVal = DatabaseUtils.sqlEscapeString(stringVal); - } - } - } - - return stringVal; - } - } - - /** - * Returns a string containing the tokens joined by delimiters and converted into the property - * values for a query. - * - * @param delimiter The text to join the text with. - * @param tokens an {@link Iterable} of objects to be joined. Strings will be formed from - * the objects by calling {@link #convertValueToString(Object, boolean)}. - * @return A joined string - */ - @NonNull - public static String joinArguments(@NonNull CharSequence delimiter, - @NonNull Iterable tokens, - @NonNull BaseOperator condition) { - StringBuilder sb = new StringBuilder(); - boolean firstTime = true; - for (Object token : tokens) { - if (firstTime) { - firstTime = false; - } else { - sb.append(delimiter); - } - sb.append(condition.convertObjectToString(token, false)); - } - return sb.toString(); - } - - /** - * Returns a string containing the tokens converted into DBValues joined by delimiters. - * - * @param delimiter The text to join the text with. - * @param tokens an array objects to be joined. Strings will be formed from - * the objects by calling object.toString(). - * @return A joined string - */ - @NonNull - public static String joinArguments(@NonNull CharSequence delimiter, @NonNull Object[] tokens) { - StringBuilder sb = new StringBuilder(); - boolean firstTime = true; - for (Object token : tokens) { - if (firstTime) { - firstTime = false; - } else { - sb.append(delimiter); - } - sb.append(convertValueToString(token, false, true)); - } - return sb.toString(); - } - - /** - * Returns a string containing the tokens converted into DBValues joined by delimiters. - * - * @param delimiter The text to join the text with. - * @param tokens an array objects to be joined. Strings will be formed from - * the objects by calling object.toString(). - * @return A joined string - */ - @NonNull - public static String joinArguments(@NonNull CharSequence delimiter, @NonNull Iterable tokens) { - StringBuilder sb = new StringBuilder(); - boolean firstTime = true; - for (Object token : tokens) { - if (firstTime) { - firstTime = false; - } else { - sb.append(delimiter); - } - sb.append(convertValueToString(token, false, true)); - } - return sb.toString(); - } - - /** - * The operation such as "=", "<", and more - */ - protected String operation = ""; - - /** - * The value of the column we care about - */ - protected Object value; - - /** - * The column name - */ - @NonNull - protected NameAlias nameAlias; - - /** - * A custom SQL statement after the value of the Operator - */ - protected String postArg; - - /** - * An optional separator to use when chaining these together - */ - protected String separator; - - /** - * If true, the value is set and we should append it. (to prevent false positive nulls) - */ - protected boolean isValueSet; - - BaseOperator(@NonNull NameAlias nameAlias) { - this.nameAlias = nameAlias; - } - - /** - * @return the value of the argument - */ - @Override - public Object value() { - return value; - } - - /** - * @return the column name - */ - @NonNull - @Override - public String columnName() { - return nameAlias.getQuery(); - } - - @NonNull - @Override - public SQLOperator separator(@NonNull String separator) { - this.separator = separator; - return this; - } - - @Nullable - @Override - public String separator() { - return separator; - } - - /** - * @return true if has a separator defined for this condition. - */ - @Override - public boolean hasSeparator() { - return separator != null && (separator.length() > 0); - } - - /** - * @return the operator such as "<", ">", or "=" - */ - @NonNull - public String operation() { - return operation; - } - - /** - * @return An optional post argument for this condition - */ - public String postArgument() { - return postArg; - } - - /** - * @return internal alias used for subclasses. - */ - NameAlias columnAlias() { - return nameAlias; - } - - public String convertObjectToString(Object object, boolean appendInnerParenthesis) { - return convertValueToString(object, appendInnerParenthesis); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java deleted file mode 100644 index ca72febec..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseQueriable.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.database.Cursor; -import android.database.sqlite.SQLiteDoneException; -import android.database.sqlite.SQLiteStatement; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.runtime.NotifyDistributor; -import com.raizlabs.android.dbflow.sql.SqlUtils; -import com.raizlabs.android.dbflow.sql.queriable.Queriable; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatementWrapper; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: Base implementation of something that can be queried from the database. - */ -public abstract class BaseQueriable implements Queriable, Actionable { - - - private final Class table; - - protected BaseQueriable(Class table) { - this.table = table; - } - - /** - * @return The table associated with this INSERT - */ - @NonNull - public Class getTable() { - return table; - } - - /** - * Execute a statement that returns a 1 by 1 table with a numeric value. - * For example, SELECT COUNT(*) FROM table. - * Please see {@link SQLiteStatement#simpleQueryForLong()}. - *

- * catches a {@link SQLiteDoneException} if result is not found and returns 0. The error can safely be ignored. - */ - @Override - public long count(@NonNull DatabaseWrapper databaseWrapper) { - return longValue(databaseWrapper); - } - - /** - * Execute a statement that returns a 1 by 1 table with a numeric value. - * For example, SELECT COUNT(*) FROM table. - * Please see {@link SQLiteStatement#simpleQueryForLong()}. - */ - @Override - public long count() { - return longValue(); - } - - @Override - public long longValue() { - return longValue(FlowManager.getWritableDatabaseForTable(table)); - } - - @Override - public long longValue(DatabaseWrapper databaseWrapper) { - try { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Executing query: " + query); - return SqlUtils.longForQuery(databaseWrapper, query); - } catch (SQLiteDoneException sde) { - // catch exception here, log it but return 0; - FlowLog.log(FlowLog.Level.W, sde); - } - return 0; - } - - @Override - public boolean hasData() { - return count() > 0; - } - - @Override - public boolean hasData(@NonNull DatabaseWrapper databaseWrapper) { - return count(databaseWrapper) > 0; - } - - @Override - public FlowCursor query() { - query(FlowManager.getWritableDatabaseForTable(table)); - return null; - } - - @Override - public FlowCursor query(@NonNull DatabaseWrapper databaseWrapper) { - if (getPrimaryAction().equals(BaseModel.Action.INSERT)) { - // inserting, let's compile and insert - DatabaseStatement databaseStatement = compileStatement(databaseWrapper); - databaseStatement.executeInsert(); - databaseStatement.close(); - } else { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Executing query: " + query); - databaseWrapper.execSQL(query); - } - return null; - } - - @Override - public long executeInsert() { - return executeInsert(FlowManager.getWritableDatabaseForTable(table)); - } - - @Override - public long executeInsert(@NonNull DatabaseWrapper databaseWrapper) { - DatabaseStatement statement = compileStatement(databaseWrapper); - long rows; - try { - rows = statement.executeInsert(); - } finally { - statement.close(); - } - return rows; - } - - @Override - public void execute() { - Cursor cursor = query(); - if (cursor != null) { - cursor.close(); - } else { - // we dont query, we're executing something here. - NotifyDistributor.get().notifyTableChanged(getTable(), getPrimaryAction()); - } - } - - @Override - public void execute(@NonNull DatabaseWrapper databaseWrapper) { - Cursor cursor = query(databaseWrapper); - if (cursor != null) { - cursor.close(); - } else { - // we dont query, we're executing something here. - NotifyDistributor.get().notifyTableChanged(getTable(), getPrimaryAction()); - } - } - - @NonNull - @Override - public DatabaseStatement compileStatement() { - return compileStatement(FlowManager.getWritableDatabaseForTable(table)); - } - - @NonNull - @Override - public DatabaseStatement compileStatement(@NonNull DatabaseWrapper databaseWrapper) { - String query = getQuery(); - FlowLog.log(FlowLog.Level.V, "Compiling Query Into Statement: " + query); - return new DatabaseStatementWrapper<>(databaseWrapper.compileStatement(query), this); - } - - @Override - public String toString() { - return getQuery(); - } - - @NonNull - public abstract BaseModel.Action getPrimaryAction(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java deleted file mode 100644 index ea768bf70..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/BaseTransformable.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.List; - -/** - * Description: Combines basic transformations and query ops into a base class. - */ -public abstract class BaseTransformable extends BaseModelQueriable implements Transformable, - WhereBase { - - /** - * Constructs new instance of this class and is meant for subclasses only. - * - * @param table the table that belongs to this query. - */ - protected BaseTransformable(Class table) { - super(table); - } - - @NonNull - public Where where(@NonNull SQLOperator... conditions) { - return new Where<>(this, conditions); - } - - @Override - public FlowCursor query() { - return where().query(); - } - - @Override - public FlowCursor query(@NonNull DatabaseWrapper databaseWrapper) { - return where().query(databaseWrapper); - } - - @NonNull - @Override - public Where groupBy(NameAlias... nameAliases) { - return where().groupBy(nameAliases); - } - - @NonNull - @Override - public Where groupBy(IProperty... properties) { - return where().groupBy(properties); - } - - @NonNull - @Override - public Where orderBy(@NonNull NameAlias nameAlias, boolean ascending) { - return where().orderBy(nameAlias, ascending); - } - - @NonNull - @Override - public Where orderBy(@NonNull IProperty property, boolean ascending) { - return where().orderBy(property, ascending); - } - - @NonNull - @Override - public Where orderByAll(@NonNull List orderBies) { - return where().orderByAll(orderBies); - } - - @NonNull - @Override - public Where orderBy(@NonNull OrderBy orderBy) { - return where().orderBy(orderBy); - } - - @NonNull - @Override - public Where limit(int count) { - return where().limit(count); - } - - @NonNull - @Override - public Where offset(int offset) { - return where().offset(offset); - } - - @NonNull - @Override - public Where having(SQLOperator... conditions) { - return where().having(conditions); - } - - @NonNull - @Override - public List queryList() { - checkSelect("query"); - return super.queryList(); - } - - @Override - public TModel querySingle() { - checkSelect("query"); - limit(1); - return super.querySingle(); - } - - private void checkSelect(String methodName) { - if (!(getQueryBuilderBase() instanceof Select)) { - throw new IllegalArgumentException("Please use " + methodName + "(). The beginning is not a Select"); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Case.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Case.java deleted file mode 100644 index f18a31924..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Case.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.sql.language.property.Property; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: Represents a SQLITE CASE argument. - */ -public class Case implements Query { - - private IProperty caseColumn; - private List> caseConditions = new ArrayList<>(); - private String columnName; - private TReturn elseValue; - private boolean elseSpecified = false; - - // when true, only WHEN value is supported. Not WHEN condition - private boolean efficientCase = false; - - private boolean endSpecified = false; - - Case() { - } - - Case(IProperty caseColumn) { - this.caseColumn = caseColumn; - if (caseColumn != null) { - efficientCase = true; - } - } - - @NonNull - public CaseCondition when(@NonNull SQLOperator sqlOperator) { - if (efficientCase) { - throw new IllegalStateException("When using the efficient CASE method," + - "you must pass in value only, not condition."); - } - CaseCondition caseCondition = new CaseCondition<>(this, sqlOperator); - caseConditions.add(caseCondition); - return caseCondition; - } - - @NonNull - public CaseCondition when(@Nullable TReturn whenValue) { - if (!efficientCase) { - throw new IllegalStateException("When not using the efficient CASE method, " + - "you must pass in the SQLOperator as a parameter"); - } - CaseCondition caseCondition = new CaseCondition<>(this, whenValue); - caseConditions.add(caseCondition); - return caseCondition; - } - - @NonNull - public CaseCondition when(@NonNull IProperty property) { - if (!efficientCase) { - throw new IllegalStateException("When not using the efficient CASE method, " + - "you must pass in the SQLOperator as a parameter"); - } - CaseCondition caseCondition = new CaseCondition<>(this, property); - caseConditions.add(caseCondition); - return caseCondition; - } - - /** - * Default case here. If not specified, value will be NULL. - */ - @NonNull - public Case _else(@Nullable TReturn elseValue) { - this.elseValue = elseValue; - elseSpecified = true; // ensure its set especially if null specified. - return this; - } - - /** - * @param columnName The name of the case that we return in a column. - * @return The case completed as a property. - */ - @NonNull - public Property> end(@Nullable String columnName) { - endSpecified = true; - if (columnName != null) { - this.columnName = QueryBuilder.quoteIfNeeded(columnName); - } - return new Property<>(null, NameAlias.rawBuilder(getQuery()) - .build()); - } - - /** - * @return The case completed as a property. - */ - @NonNull - public Property> end() { - return end(null); - } - - /** - * @return The case complete as an operator. - */ - @NonNull - public Operator endAsOperator() { - return Operator.op(end().getNameAlias()); - } - - boolean isEfficientCase() { - return efficientCase; - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder(" CASE"); - if (isEfficientCase()) { - queryBuilder.append(" " + BaseOperator.convertValueToString(caseColumn, false)); - } - - queryBuilder.append(QueryBuilder.join("", caseConditions)); - - if (elseSpecified) { - queryBuilder.append(" ELSE ").append(BaseOperator.convertValueToString(elseValue, false)); - } - if (endSpecified) { - queryBuilder.append(" END " + (columnName != null ? columnName : "")); - } - return queryBuilder.getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CaseCondition.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CaseCondition.java deleted file mode 100644 index 400fa818f..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CaseCondition.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; - -import static com.raizlabs.android.dbflow.sql.language.BaseOperator.convertValueToString; - -/** - * Description: Represents an individual condition inside a CASE. - */ -public class CaseCondition implements Query { - - private final Case caze; - private TReturn whenValue; - private SQLOperator sqlOperator; - private TReturn thenValue; - private IProperty property; - private IProperty thenProperty; - private boolean isThenPropertySet; - - CaseCondition(Case caze, @NonNull SQLOperator sqlOperator) { - this.caze = caze; - this.sqlOperator = sqlOperator; - } - - CaseCondition(Case caze, TReturn whenValue) { - this.caze = caze; - this.whenValue = whenValue; - } - - CaseCondition(Case caze, @NonNull IProperty property) { - this.caze = caze; - this.property = property; - } - - /** - * THEN part of this query, the value that gets set on column if condition is true. - */ - @NonNull - public Case then(@Nullable TReturn value) { - thenValue = value; - return caze; - } - - @NonNull - public Case then(@NonNull IProperty value) { - thenProperty = value; - // in case values are null in some sense. - isThenPropertySet = true; - return caze; - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder(" WHEN "); - if (caze.isEfficientCase()) { - queryBuilder.append(convertValueToString(property != null ? property : whenValue, false)); - } else { - sqlOperator.appendConditionToQuery(queryBuilder); - } - queryBuilder.append(" THEN ") - .append(convertValueToString(isThenPropertySet ? - thenProperty : thenValue, false)); - return queryBuilder.getQuery(); - } - - @Override - public String toString() { - return getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CompletedTrigger.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CompletedTrigger.java deleted file mode 100644 index 135ff6c3d..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CompletedTrigger.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.SqlUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: The last piece of a TRIGGER statement, this class contains the BEGIN...END and the logic in between. - */ -public class CompletedTrigger implements Query { - - /** - * The first pieces of this TRIGGER statement - */ - private TriggerMethod triggerMethod; - - /** - * The query to run between the BEGIN and END of this statement - */ - private final List triggerLogicQuery = new ArrayList<>(); - - CompletedTrigger(TriggerMethod triggerMethod, Query triggerLogicQuery) { - this.triggerMethod = triggerMethod; - this.triggerLogicQuery.add(triggerLogicQuery); - } - - /** - * Appends the nextStatement to this query as another line to be executed by trigger. - */ - @NonNull - public CompletedTrigger and(@NonNull Query nextStatement) { - this.triggerLogicQuery.add(nextStatement); - return this; - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder(triggerMethod.getQuery()); - queryBuilder.append("\nBEGIN") - .append("\n").append(QueryBuilder.join(";\n", triggerLogicQuery)).append(";") - .append("\nEND"); - return queryBuilder.getQuery(); - } - - - /** - * Turns on this trigger - */ - public void enable() { - DatabaseDefinition databaseDefinition = FlowManager.getDatabaseForTable(triggerMethod.onTable); - databaseDefinition.getWritableDatabase().execSQL(getQuery()); - } - - /** - * Disables this trigger - */ - public void disable() { - SqlUtils.dropTrigger(triggerMethod.onTable, triggerMethod.trigger.triggerName); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CursorResult.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CursorResult.java deleted file mode 100644 index 27ff0ce74..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/CursorResult.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.list.FlowCursorIterator; -import com.raizlabs.android.dbflow.list.IFlowCursorIterator; -import com.raizlabs.android.dbflow.structure.InstanceAdapter; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: A class that contains a {@link Cursor} and handy methods for retrieving data from it. - * You must close this object post use via {@link #close()}. - */ -public class CursorResult implements IFlowCursorIterator { - - private final InstanceAdapter retrievalAdapter; - - @Nullable - private FlowCursor cursor; - - @SuppressWarnings("unchecked") - CursorResult(Class modelClass, @Nullable Cursor cursor) { - if (cursor != null) { - this.cursor = FlowCursor.from(cursor); - } - retrievalAdapter = FlowManager.getInstanceAdapter(modelClass); - } - - /** - * Swaps the current cursor and will close existing one. - */ - public void swapCursor(@Nullable FlowCursor cursor) { - if (this.cursor != null) { - if (!this.cursor.isClosed()) { - this.cursor.close(); - } - } - this.cursor = cursor; - } - - /** - * @return A {@link List} of items from this object. You must call {@link #close()} when finished. - */ - @NonNull - public List toList() { - return cursor != null - ? retrievalAdapter.getListModelLoader().convertToData(cursor, null) - : new ArrayList(); - } - - /** - * @return Converts the {@link Cursor} to a {@link List} of {@link TModel} and then closes it. - */ - @NonNull - public List toListClose() { - final List list = cursor != null - ? retrievalAdapter.getListModelLoader().load(cursor) - : new ArrayList(); - close(); - return list; - } - - /** - * @return A {@link List} of items from this object. You must call {@link #close()} when finished. - */ - @NonNull - public List toCustomList(@NonNull Class customClass) { - return cursor != null ? FlowManager.getQueryModelAdapter(customClass) - .getListModelLoader().convertToData(cursor, null) : new ArrayList(); - } - - /** - * @return Converts the {@link Cursor} to a {@link List} of {@link TModel} and then closes it. - */ - @NonNull - public List toCustomListClose(@NonNull Class customClass) { - final List customList = cursor != null ? FlowManager.getQueryModelAdapter(customClass) - .getListModelLoader().load(cursor) : new ArrayList(); - close(); - return customList; - } - - /** - * @return The first {@link TModel} of items from the contained {@link Cursor}. You must call {@link #close()} when finished. - */ - @Nullable - public TModel toModel() { - return cursor != null ? retrievalAdapter.getSingleModelLoader().convertToData(cursor, null) : null; - } - - /** - * @return Converts the {@link Cursor} into the first {@link TModel} from the cursor and then closes it. - */ - @Nullable - public TModel toModelClose() { - final TModel model = cursor != null ? retrievalAdapter.getSingleModelLoader().load(cursor) : null; - close(); - return model; - } - - /** - * @return The first {@link TModel} of items from the contained {@link Cursor}. You must call {@link #close()} when finished. - */ - @Nullable - public TCustom toCustomModel(@NonNull Class customClass) { - return cursor != null ? FlowManager.getQueryModelAdapter(customClass) - .getSingleModelLoader().convertToData(cursor, null) : null; - } - - /** - * @return Converts the {@link Cursor} to a {@link TModel} and then closes it. - */ - @Nullable - public TCustom toCustomModelClose(@NonNull Class customClass) { - final TCustom customList = cursor != null ? FlowManager.getQueryModelAdapter(customClass) - .getSingleModelLoader().load(cursor) : null; - close(); - return customList; - } - - @Nullable - @Override - public TModel getItem(long position) { - TModel model = null; - if (cursor != null && cursor.moveToPosition((int) position)) { - model = retrievalAdapter.getSingleModelLoader().convertToData(cursor, null, false); - } - return model; - } - - @NonNull - @Override - public FlowCursorIterator iterator() { - return new FlowCursorIterator<>(this); - } - - @NonNull - @Override - public FlowCursorIterator iterator(int startingLocation, long limit) { - return new FlowCursorIterator<>(this, startingLocation, limit); - } - - @Override - public long getCount() { - return cursor == null ? 0 : cursor.getCount(); - } - - @Override - @Nullable - public Cursor cursor() { - return cursor; - } - - @Override - public void close() { - if (cursor != null) { - cursor.close(); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Delete.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Delete.java deleted file mode 100644 index 5e2048d21..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Delete.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; - -/** - * Description: Constructs the beginning of a SQL DELETE query - */ -public class Delete implements Query { - - /** - * Deletes the specified table - * - * @param table The table to delete - * @param conditions The list of conditions to use to delete from the specified table - * @param The class that implements {@link com.raizlabs.android.dbflow.structure.Model} - */ - public static void table(@NonNull Class table, SQLOperator... conditions) { - new Delete().from(table).where(conditions).executeUpdateDelete(); - } - - /** - * Deletes the list of tables specified. - * WARNING: this will completely clear all rows from each table. - * - * @param tables The list of tables to wipe. - */ - public static void tables(Class... tables) { - for (Class modelClass : tables) { - table(modelClass); - } - } - - /** - * Returns the new SQL FROM statement wrapper - * - * @param table The table we want to run this query from - * @param The table class - * @return - */ - @NonNull - public From from(@NonNull Class table) { - return new From<>(this, table); - } - - @Override - public String getQuery() { - return new QueryBuilder() - .append("DELETE") - .appendSpace().getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/ExistenceOperator.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/ExistenceOperator.java deleted file mode 100644 index ae5786307..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/ExistenceOperator.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; - -/** - * Description: The condition that represents EXISTS in a SQL statement. - */ -public class ExistenceOperator implements SQLOperator, Query { - - private Where innerWhere; - - @Override - public void appendConditionToQuery(@NonNull QueryBuilder queryBuilder) { - queryBuilder.appendQualifier("EXISTS", "(" + innerWhere.getQuery().trim() + ")"); - } - - @NonNull - @Override - public String columnName() { - throw new RuntimeException("Method not valid for ExistenceOperator"); - } - - @Nullable - @Override - public String separator() { - throw new RuntimeException("Method not valid for ExistenceOperator"); - } - - @NonNull - @Override - public SQLOperator separator(@NonNull String separator) { - // not used. - throw new RuntimeException("Method not valid for ExistenceOperator"); - } - - @Override - public boolean hasSeparator() { - return false; - } - - @NonNull - @Override - public String operation() { - return ""; - } - - @Override - public Object value() { - return innerWhere; - } - - public ExistenceOperator where(@NonNull Where where) { - this.innerWhere = where; - return this; - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder(); - appendConditionToQuery(queryBuilder); - return queryBuilder.getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java deleted file mode 100644 index 57a60389c..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/From.java +++ /dev/null @@ -1,249 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.Join.JoinType; -import com.raizlabs.android.dbflow.sql.language.property.IndexProperty; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.BaseModel; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; - -/** - * Description: The SQL FROM query wrapper that must have a {@link Query} base. - */ -public class From extends BaseTransformable { - - /** - * The base such as {@link Delete}, {@link Select} and more! - */ - @NonNull - private Query queryBase; - - /** - * An alias for the table - */ - @Nullable - private NameAlias tableAlias; - - /** - * Enables the SQL JOIN statement - */ - @NonNull - private final List joins = new ArrayList<>(); - - private NameAlias getTableAlias() { - if (tableAlias == null) { - tableAlias = new NameAlias.Builder(FlowManager.getTableName(getTable())).build(); - } - return tableAlias; - } - - /** - * The SQL from statement constructed. - * - * @param querybase The base query we append this query to - * @param table The table this corresponds to - */ - public From(@NonNull Query querybase, @NonNull Class table) { - super(table); - queryBase = querybase; - } - - /** - * Set an alias to the table name of this {@link From}. - */ - @NonNull - public From as(String alias) { - tableAlias = getTableAlias() - .newBuilder() - .as(alias) - .build(); - return this; - } - - /** - * Adds a join on a specific table for this query - * - * @param table The table this corresponds to - * @param joinType The type of join to use - */ - @NonNull - public Join join(Class table, @NonNull JoinType joinType) { - Join join = new Join<>(this, table, joinType); - joins.add(join); - return join; - } - - /** - * Adds a join on a specific table for this query. - * - * @param modelQueriable A query we construct the {@link Join} from. - * @param joinType The type of join to use. - */ - @NonNull - public Join - join(ModelQueriable modelQueriable, @NonNull JoinType joinType) { - Join join = new Join<>(this, joinType, modelQueriable); - joins.add(join); - return join; - } - - /** - * Adds a {@link JoinType#CROSS} join on a specific table for this query. - * - * @param table The table to join on. - * @param The class of the join table. - */ - @NonNull - public Join crossJoin(Class table) { - return join(table, JoinType.CROSS); - } - - /** - * Adds a {@link JoinType#CROSS} join on a specific table for this query. - * - * @param modelQueriable The query to join on. - * @param The class of the join table. - */ - @NonNull - public Join crossJoin(ModelQueriable modelQueriable) { - return join(modelQueriable, JoinType.CROSS); - } - - /** - * Adds a {@link JoinType#INNER} join on a specific table for this query. - * - * @param table The table to join on. - * @param The class of the join table. - */ - @NonNull - public Join innerJoin(Class table) { - return join(table, JoinType.INNER); - } - - /** - * Adds a {@link JoinType#INNER} join on a specific table for this query. - * - * @param modelQueriable The query to join on. - * @param The class of the join table. - */ - @NonNull - public Join innerJoin(ModelQueriable modelQueriable) { - return join(modelQueriable, JoinType.INNER); - } - - /** - * Adds a {@link JoinType#LEFT_OUTER} join on a specific table for this query. - * - * @param table The table to join on. - * @param The class of the join table. - */ - @NonNull - public Join leftOuterJoin(Class table) { - return join(table, JoinType.LEFT_OUTER); - } - - /** - * Adds a {@link JoinType#LEFT_OUTER} join on a specific table for this query. - * - * @param modelQueriable The query to join on. - * @param The class of the join table. - */ - @NonNull - public Join leftOuterJoin(ModelQueriable modelQueriable) { - return join(modelQueriable, JoinType.LEFT_OUTER); - } - - - /** - * Adds a {@link JoinType#NATURAL} join on a specific table for this query. - * - * @param table The table to join on. - * @param The class of the join table. - */ - @NonNull - public Join naturalJoin(Class table) { - return join(table, JoinType.NATURAL); - } - - /** - * Adds a {@link JoinType#NATURAL} join on a specific table for this query. - * - * @param modelQueriable The query to join on. - * @param The class of the join table. - */ - @NonNull - public Join naturalJoin(ModelQueriable modelQueriable) { - return join(modelQueriable, JoinType.NATURAL); - } - - /** - * Begins an INDEXED BY piece of this query with the specified name. - * - * @param indexProperty The index property generated. - */ - @NonNull - public IndexedBy indexedBy(IndexProperty indexProperty) { - return new IndexedBy<>(indexProperty, this); - } - - @NonNull - @Override - public BaseModel.Action getPrimaryAction() { - return (queryBase instanceof Delete) ? BaseModel.Action.DELETE : BaseModel.Action.CHANGE; - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder() - .append(queryBase.getQuery()); - if (!(queryBase instanceof Update)) { - queryBuilder.append("FROM "); - } - - queryBuilder.append(getTableAlias()); - - if (queryBase instanceof Select) { - if (!joins.isEmpty()) { - queryBuilder.appendSpace(); - } - for (Join join : joins) { - queryBuilder.append(join.getQuery()); - } - } else { - queryBuilder.appendSpace(); - } - - return queryBuilder.getQuery(); - } - - /** - * @return The base query, usually a {@link Delete}, {@link Select}, or {@link Update} - */ - @NonNull - @Override - public Query getQueryBuilderBase() { - return queryBase; - } - - /** - * @return A list of {@link Class} that represents tables represented in this query. For every - * {@link Join} on another table, this adds another {@link Class}. - */ - @NonNull - public java.util.Set> getAssociatedTables() { - java.util.Set> tables = new LinkedHashSet<>(); - tables.add(getTable()); - for (Join join : joins) { - tables.add(join.getTable()); - } - return tables; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IConditional.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IConditional.java deleted file mode 100644 index 817c55c28..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IConditional.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.Query; - -/** - * Description: Simple interface for objects that can be used as {@link Operator}. This class - * takes no type parameters for primitive objects. - */ -public interface IConditional extends Query { - - @NonNull - Operator is(@NonNull IConditional conditional); - - @NonNull - Operator is(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator isNull(); - - @NonNull - Operator eq(@NonNull IConditional conditional); - - @NonNull - Operator eq(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator isNotNull(); - - @NonNull - Operator concatenate(@NonNull IConditional conditional); - - @NonNull - Operator isNot(@NonNull IConditional conditional); - - @NonNull - Operator isNot(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator notEq(@NonNull IConditional conditional); - - @NonNull - Operator notEq(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator like(@NonNull IConditional conditional); - - @NonNull - Operator like(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator notLike(@NonNull IConditional conditional); - - @NonNull - Operator notLike(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator glob(@NonNull IConditional conditional); - - @NonNull - Operator glob(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator like(@NonNull String value); - - @NonNull - Operator notLike(@NonNull String value); - - @NonNull - Operator glob(@NonNull String value); - - @NonNull - Operator greaterThan(@NonNull IConditional conditional); - - @NonNull - Operator greaterThan(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator greaterThanOrEq(@NonNull IConditional conditional); - - @NonNull - Operator greaterThanOrEq(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator lessThan(@NonNull IConditional conditional); - - @NonNull - Operator lessThan(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator lessThanOrEq(@NonNull IConditional conditional); - - @NonNull - Operator lessThanOrEq(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator.Between between(@NonNull IConditional conditional); - - @NonNull - Operator.Between between(@NonNull BaseModelQueriable baseModelQueriable); - - @NonNull - Operator.In in(@NonNull IConditional firstConditional, @NonNull IConditional... conditionals); - - @NonNull - Operator.In in(@NonNull BaseModelQueriable firstBaseModelQueriable, - @NonNull BaseModelQueriable... baseModelQueriables); - - @NonNull - Operator.In notIn(@NonNull IConditional firstConditional, @NonNull IConditional... conditionals); - - @NonNull - Operator.In notIn(@NonNull BaseModelQueriable firstBaseModelQueriable, - @NonNull BaseModelQueriable... baseModelQueriables); - - @NonNull - Operator plus(@NonNull BaseModelQueriable value); - - @NonNull - Operator minus(@NonNull BaseModelQueriable value); - - @NonNull - Operator div(@NonNull BaseModelQueriable value); - - @NonNull - Operator times(@NonNull BaseModelQueriable value); - - @NonNull - Operator rem(@NonNull BaseModelQueriable value); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IOperator.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IOperator.java deleted file mode 100644 index 701b6bb07..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IOperator.java +++ /dev/null @@ -1,187 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.Query; - -import java.util.Collection; - -/** - * Description: Interface for objects that can be used as {@link Operator} that have a type parameter. - */ -public interface IOperator extends Query, IConditional { - - /** - * Assigns the operation to "=" - * - * @param value The {@link T} that we express equality to. - * @return A {@link Operator} that represents equality between this and the parameter. - */ - @NonNull - Operator is(@Nullable T value); - - /** - * Assigns the operation to "=". Identical to {@link #is(T)} - * - * @param value The {@link T} that we express equality to. - * @return A {@link Operator} that represents equality between this and the parameter. - * @see #is(T) - */ - @NonNull - Operator eq(@Nullable T value); - - /** - * Generates a {@link Operator} that concatenates this {@link IOperator} with the {@link T} via "||" - * by columnName=columnName || value - * - * @param value The value to concatenate. - * @return A {@link Operator} that represents concatenation. - */ - @NonNull - Operator concatenate(@Nullable T value); - - /** - * Assigns the operation to "!=" - * - * @param value The {@link T} that we express inequality to. - * @return A {@link Operator} that represents inequality between this and the parameter. - */ - @NonNull - Operator isNot(@Nullable T value); - - /** - * Assigns the operation to "!=" - * - * @param value The {@link T} that we express inequality to. - * @return A {@link Operator} that represents inequality between this and the parameter. - * @see #notEq(T) - */ - @NonNull - Operator notEq(@Nullable T value); - - /** - * Assigns operation to ">" - * - * @param value The {@link T} that this {@link IOperator} is greater than. - * @return A {@link Operator} that represents greater than between this and the parameter. - */ - @NonNull - Operator greaterThan(@NonNull T value); - - /** - * Assigns operation to ">=" - * - * @param value The {@link T} that this {@link IOperator} is greater than or equal to. - * @return A {@link Operator} that represents greater than or equal between this and the parameter. - */ - @NonNull - Operator greaterThanOrEq(@NonNull T value); - - - /** - * Assigns operation to "<" - * - * @param value The {@link T} that this {@link IOperator} is less than. - * @return A {@link Operator} that represents less than between this and the parameter. - */ - @NonNull - Operator lessThan(@NonNull T value); - - - /** - * Assigns operation to "<=" - * - * @param value The {@link T} that this {@link IOperator} is less than or equal to. - * @return A {@link Operator} that represents less than or equal to between this and the parameter. - */ - @NonNull - Operator lessThanOrEq(@NonNull T value); - - @NonNull - Operator.Between between(@NonNull T value); - - /** - * Turns this {@link IOperator} into an {@link Operator.In}. It means that this object should - * be represented by the set of {@link T} provided to follow. - * - * @param firstValue The first value (required to enforce >= 1) - * @param values The rest of the values to pass optionally. - * @return A new {@link Operator.In} built from this {@link IOperator}. - */ - @NonNull - @SuppressWarnings("unchecked") - Operator.In in(@NonNull T firstValue, T... values); - - /** - * Turns this {@link IOperator} into an {@link Operator.In} (not). It means that this object should NOT - * be represented by the set of {@link T} provided to follow. - * - * @param firstValue The first value (required to enforce >= 1) - * @param values The rest of the values to pass optionally. - * @return A new {@link Operator.In} (not) built from this {@link IOperator}. - */ - @NonNull - @SuppressWarnings("unchecked") - Operator.In notIn(@NonNull T firstValue, T... values); - - /** - * Turns this {@link IOperator} into an {@link Operator.In}. It means that this object should - * be represented by the set of {@link T} provided to follow. - * - * @param values The rest of the values to pass optionally. - * @return A new {@link Operator.In} built from this {@link IOperator}. - */ - @NonNull - Operator.In in(@NonNull Collection values); - - /** - * Turns this {@link IOperator} into an {@link Operator.In} (not). It means that this object should NOT - * be represented by the set of {@link T} provided to follow. - * - * @param values The rest of the values to pass optionally. - * @return A new {@link Operator.In} (not) built from this {@link IOperator}. - */ - @NonNull - Operator.In notIn(@NonNull Collection values); - - /** - * Adds another value and returns the operator. i.e p1 + p2 - * - * @param value the value to add. - */ - @NonNull - Operator plus(@NonNull T value); - - /** - * Subtracts another value and returns the operator. i.e p1 - p2 - * - * @param value the value to subtract. - */ - @NonNull - Operator minus(@NonNull T value); - - /** - * Divides another value and returns as the operator. i.e p1 / p2 - * - * @param value the value to divide. - * @return A new instance. - */ - @NonNull - Operator div(@NonNull T value); - - /** - * Multiplies another value and returns as the operator. i.e p1 * p2 - * - * @param value the value to multiply. - */ - Operator times(@NonNull T value); - - /** - * Modulous another value and returns as the operator. i.e p1 % p2 - * - * @param value the value to calculate remainder of. - */ - @NonNull - Operator rem(@NonNull T value); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Index.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Index.java deleted file mode 100644 index c79f5e476..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Index.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.SqlUtils; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: an INDEX class that enables you to index a specific column from a table. This enables - * faster retrieval on tables, while increasing the database file size. So enable/disable these as necessary. - */ -public class Index implements Query { - - @NonNull - private final String indexName; - - @NonNull - private Class table; - private List columns; - private boolean isUnique = false; - - /** - * Creates a new index with the specified name - * - * @param indexName The name of this index. - */ - public Index(@NonNull String indexName) { - this.indexName = indexName; - columns = new ArrayList<>(); - } - - /** - * If true, will append the UNIQUE statement to this trigger. - * - * @param unique true if unique. If created again, a {@link android.database.SQLException} is thrown. - * @return This instance. - */ - @NonNull - public Index unique(boolean unique) { - isUnique = unique; - return this; - } - - /** - * The table to execute this Index on. - * - * @param table The table to execute index on. - * @param properties The properties to create an index for. - * @return This instance. - */ - @NonNull - public Index on(@NonNull Class table, IProperty... properties) { - this.table = table; - for (IProperty property : properties) { - and(property); - } - return this; - } - - /** - * The table to execute this Index on. - * - * @param table The table to execute index on. - * @param columns The columns to create an index for. - * @return This instance. - */ - @NonNull - public Index on(@NonNull Class table, @NonNull NameAlias firstAlias, NameAlias... columns) { - this.table = table; - and(firstAlias); - for (NameAlias column : columns) { - and(column); - } - return this; - } - - /** - * Appends a column to this index list. - * - * @param property The name of the column. If already exists, this op will not be added - * @return This instance. - */ - @NonNull - public Index and(@NonNull IProperty property) { - if (!columns.contains(property.getNameAlias())) { - columns.add(property.getNameAlias()); - } - return this; - } - - /** - * Appends a column to this index list. - * - * @param columnName The name of the column. If already exists, this op will not be added - * @return This instance. - */ - @NonNull - public Index and(@NonNull NameAlias columnName) { - if (!columns.contains(columnName)) { - columns.add(columnName); - } - return this; - } - - /** - * @return The name of this index. - */ - @NonNull - public String getIndexName() { - return indexName; - } - - /** - * @return The table this INDEX belongs to. - */ - @NonNull - public Class getTable() { - return table; - } - - /** - * @return true if the index is unique - */ - public boolean isUnique() { - return isUnique; - } - - public void enable(@NonNull DatabaseWrapper databaseWrapper) { - if (table == null) { - throw new IllegalStateException("Please call on() to set a table to use this index on."); - } else if (columns == null || columns.isEmpty()) { - throw new IllegalStateException("There should be at least one column in this index"); - } - databaseWrapper.execSQL(getQuery()); - } - - public void enable() { - enable(FlowManager.getDatabaseForTable(table).getWritableDatabase()); - } - - public void disable() { - SqlUtils.dropIndex(FlowManager.getDatabaseForTable(table).getWritableDatabase(), indexName); - } - - public void disable(DatabaseWrapper databaseWrapper) { - SqlUtils.dropIndex(databaseWrapper, indexName); - } - - @Override - @SuppressWarnings("unchecked") - public String getQuery() { - return new QueryBuilder("CREATE ") - .append(isUnique ? "UNIQUE " : "") - .append("INDEX IF NOT EXISTS ") - .appendQuotedIfNeeded(indexName) - .append(" ON ").append(FlowManager.getTableName(table)) - .append("(").appendList(columns).append(")").getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IndexedBy.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IndexedBy.java deleted file mode 100644 index b585984da..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/IndexedBy.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IndexProperty; -import com.raizlabs.android.dbflow.structure.BaseModel; - -/** - * Description: The INDEXED BY part of a SELECT/UPDATE/DELETE - */ -public class IndexedBy extends BaseTransformable { - - private final IndexProperty indexProperty; - - private final WhereBase whereBase; - - /** - * Creates the INDEXED BY part of the clause. - * - * @param indexProperty The index property generated. - * @param whereBase The base piece of this query - */ - public IndexedBy(IndexProperty indexProperty, WhereBase whereBase) { - super(whereBase.getTable()); - this.indexProperty = indexProperty; - this.whereBase = whereBase; - } - - @NonNull - @Override - public Query getQueryBuilderBase() { - return whereBase.getQueryBuilderBase(); - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder(whereBase.getQuery()) - .append(" INDEXED BY ").append(QueryBuilder.quoteIfNeeded(indexProperty.getIndexName())).appendSpace(); - return queryBuilder.getQuery(); - } - - @NonNull - @Override - public BaseModel.Action getPrimaryAction() { - return whereBase.getPrimaryAction(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Insert.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Insert.java deleted file mode 100644 index 8a3ffd590..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Insert.java +++ /dev/null @@ -1,332 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.content.ContentValues; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.ConflictAction; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Description: The SQLite INSERT command - */ -public class Insert extends BaseQueriable implements Query { - - - /** - * The columns to specify in this query (optional) - */ - private IProperty[] columns; - - /** - * The values to specify in this query - */ - private List> valuesList; - - /** - * The conflict algorithm to use to resolve inserts. - */ - private ConflictAction conflictAction = ConflictAction.NONE; - - private From selectFrom; - - /** - * Constructs a new INSERT command - * - * @param table The table to insert into - */ - public Insert(@NonNull Class table) { - super(table); - } - - /** - * The optional columns to specify. If specified, the values length must correspond to these columns, and - * each column has a 1-1 relationship to the values. - * - * @param columns The columns to use - */ - @NonNull - public Insert columns(@NonNull String... columns) { - this.columns = new IProperty[columns.length]; - ModelAdapter modelClassModelAdapter = FlowManager.getModelAdapter(getTable()); - for (int i = 0; i < columns.length; i++) { - String column = columns[i]; - this.columns[i] = modelClassModelAdapter.getProperty(column); - } - return this; - } - - @NonNull - public Insert columns(@NonNull IProperty... properties) { - this.columns = new IProperty[properties.length]; - for (int i = 0; i < properties.length; i++) { - columns[i] = properties[i]; - } - return this; - } - - @NonNull - public Insert columns(@NonNull List properties) { - return columns(properties.toArray(new IProperty[properties.size()])); - } - - /** - * @return Appends a list of columns to this INSERT statement from the associated {@link TModel}. - */ - @NonNull - public Insert asColumns() { - columns(FlowManager.getModelAdapter(getTable()).getAllColumnProperties()); - return this; - } - - /** - * @return Appends a list of columns to this INSERT and ? as the values. - */ - @NonNull - public Insert asColumnValues() { - asColumns(); - if (columns != null) { - List values = new ArrayList<>(); - for (int i = 0; i < columns.length; i++) { - values.add("?"); - } - valuesList.add(values); - } - return this; - } - - /** - * The required values to specify. It must be non-empty and match the length of the columns when - * a set of columns are specified. - * - * @param values The non type-converted values - */ - @NonNull - public Insert values(@Nullable Object... values) { - if (this.valuesList == null) { - this.valuesList = new ArrayList<>(); - } - this.valuesList.add(Arrays.asList(values)); - return this; - } - - /** - * The required values to specify. It must be non-empty and match the length of the columns when - * a set of columns are specified. - * - * @param values The non type-converted values - */ - @NonNull - public Insert values(@NonNull Collection values) { - if (this.valuesList == null) { - this.valuesList = new ArrayList<>(); - } - this.valuesList.add(values); - return this; - } - - /** - * Uses the {@link Operator} pairs to fill this insert query. - * - * @param conditions The conditions that we use to fill the columns and values of this INSERT - */ - @NonNull - public Insert columnValues(@NonNull SQLOperator... conditions) { - - String[] columns = new String[conditions.length]; - Object[] values = new Object[conditions.length]; - - for (int i = 0; i < conditions.length; i++) { - SQLOperator condition = conditions[i]; - columns[i] = condition.columnName(); - values[i] = condition.value(); - } - - return columns(columns).values(values); - } - - /** - * Uses the {@link Operator} pairs to fill this insert query. - * - * @param operatorGroup The OperatorGroup to use - */ - @NonNull - public Insert columnValues(@NonNull OperatorGroup operatorGroup) { - - int size = operatorGroup.size(); - String[] columns = new String[size]; - Object[] values = new Object[size]; - - for (int i = 0; i < size; i++) { - SQLOperator condition = operatorGroup.getConditions().get(i); - columns[i] = condition.columnName(); - values[i] = condition.value(); - } - - return columns(columns).values(values); - } - - @NonNull - public Insert columnValues(@NonNull ContentValues contentValues) { - java.util.Set> entries = contentValues.valueSet(); - int count = 0; - String[] columns = new String[contentValues.size()]; - Object[] values = new Object[contentValues.size()]; - for (Map.Entry entry : entries) { - String key = entry.getKey(); - columns[count] = key; - values[count] = contentValues.get(key); - count++; - } - - return columns(columns).values(values); - } - - /** - * Appends the specified {@link From}, which comes from a {@link Select} statement. - * - * @param selectFrom The from that is continuation of {@link Select}. - */ - @NonNull - public Insert select(@NonNull From selectFrom) { - this.selectFrom = selectFrom; - return this; - } - - - /** - * Specifies the optional OR method to use for this insert query - * - * @param action The conflict action to use - * @return - */ - @NonNull - public Insert or(@NonNull ConflictAction action) { - conflictAction = action; - return this; - } - - /** - * Specifies OR REPLACE, which will either insert if row does not exist, or replace the value if it does. - * - * @return - */ - @NonNull - public Insert orReplace() { - return or(ConflictAction.REPLACE); - } - - /** - * Specifies OR ROLLBACK, which will cancel the current transaction or ABORT the current statement. - * - * @return - */ - @NonNull - public Insert orRollback() { - return or(ConflictAction.ROLLBACK); - } - - /** - * Specifies OR ABORT, which will cancel the current INSERT, but all other operations will be preserved in - * the current transaction. - * - * @return - */ - @NonNull - public Insert orAbort() { - return or(ConflictAction.ABORT); - } - - /** - * Specifies OR FAIL, which does not back out of the previous statements. Anything else in the current - * transaction will fail. - * - * @return - */ - @NonNull - public Insert orFail() { - return or(ConflictAction.FAIL); - } - - /** - * Specifies OR IGNORE, which ignores any kind of error and proceeds as normal. - * - * @return - */ - @NonNull - public Insert orIgnore() { - return or(ConflictAction.IGNORE); - } - - @Override - public long executeUpdateDelete(@NonNull DatabaseWrapper databaseWrapper) { - throw new IllegalStateException("Cannot call executeUpdateDelete() from an Insert"); - } - - @Override - public long executeUpdateDelete() { - throw new IllegalStateException("Cannot call executeUpdateDelete() from an Insert"); - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder("INSERT "); - if (conflictAction != null && !conflictAction.equals(ConflictAction.NONE)) { - queryBuilder.append("OR").appendSpaceSeparated(conflictAction); - } - queryBuilder.append("INTO") - .appendSpace() - .append(FlowManager.getTableName(getTable())); - - if (columns != null) { - queryBuilder.append("(") - .appendArray((Object[]) columns) - .append(")"); - } - - // append FROM, which overrides values - if (selectFrom != null) { - queryBuilder.appendSpace().append(selectFrom.getQuery()); - } else { - if (valuesList == null || valuesList.size() < 1) { - throw new IllegalStateException("The insert of " + FlowManager.getTableName(getTable()) + " should have" + - "at least one value specified for the insert"); - } else if (columns != null) { - for (Collection values : valuesList) { - if (values.size() != columns.length) { - throw new IllegalStateException("The Insert of " + FlowManager.getTableName(getTable()) + " when specifying" + - "columns needs to have the same amount of values and columns"); - } - } - } - - queryBuilder.append(" VALUES("); - for (int i = 0; i < valuesList.size(); i++) { - if (i > 0) { - queryBuilder.append(",("); - } - queryBuilder.append(BaseOperator.joinArguments(", ", valuesList.get(i))).append(")"); - } - } - - return queryBuilder.getQuery(); - } - - @NonNull - @Override - public BaseModel.Action getPrimaryAction() { - return BaseModel.Action.INSERT; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Join.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Join.java deleted file mode 100644 index b607935f6..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Join.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.sql.language.property.PropertyFactory; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Description: Specifies a SQLite JOIN statement - */ -public class Join implements Query { - - /** - * The specific type of JOIN that is used. - */ - public enum JoinType { - - /** - * an extension of the INNER JOIN. Though SQL standard defines three types of OUTER JOINs: LEFT, RIGHT, - * and FULL but SQLite only supports the LEFT OUTER JOIN. - *

- * The OUTER JOINs have a condition that is identical to INNER JOINs, expressed using an ON, USING, or NATURAL keyword. - * The initial results table is calculated the same way. Once the primary JOIN is calculated, - * an OUTER join will take any unjoined rows from one or both tables, pad them out with NULLs, - * and append them to the resulting table. - */ - LEFT_OUTER, - - /** - * creates a new result table by combining column values of two tables (table1 and table2) based upon the join-predicate. - * The query compares each row of table1 with each row of table2 to find all pairs of rows which satisfy the join-predicate. - * When the join-predicate is satisfied, column values for each matched pair of rows of A and B are combined into a result row - */ - INNER, - - /** - * matches every row of the first table with every row of the second table. If the input tables - * have x and y columns, respectively, the resulting table will have x*y columns. - * Because CROSS JOINs have the potential to generate extremely large tables, - * care must be taken to only use them when appropriate. - */ - CROSS, - - /** - * a join that performs the same task as an INNER or LEFT JOIN, in which the ON or USING - * clause refers to all columns that the tables to be joined have in common. - */ - NATURAL - } - - private final Class table; - /** - * The type of JOIN to use - */ - private JoinType type; - - /** - * The FROM statement that prefixes this statement. - */ - private From from; - - /** - * The alias to name the JOIN - */ - private NameAlias alias; - - /** - * The ON conditions - */ - private OperatorGroup onGroup; - - /** - * What columns to use. - */ - private List using = new ArrayList<>(); - - public Join(@NonNull From from, @NonNull Class table, @NonNull JoinType joinType) { - this.from = from; - this.table = table; - type = joinType; - alias = new NameAlias.Builder(FlowManager.getTableName(table)).build(); - } - - public Join(@NonNull From from, @NonNull JoinType joinType, - @NonNull ModelQueriable modelQueriable) { - table = modelQueriable.getTable(); - this.from = from; - type = joinType; - alias = PropertyFactory.from(modelQueriable).getNameAlias(); - } - - /** - * Specifies if the JOIN has a name it should be called. - * - * @param alias The name to give it - * @return This instance - */ - @NonNull - public Join as(@NonNull String alias) { - this.alias = this.alias - .newBuilder() - .as(alias) - .build(); - return this; - } - - /** - * Specify the conditions that the JOIN is on - * - * @param onConditions The conditions it is on - * @return The FROM that this JOIN came from - */ - @NonNull - public From on(SQLOperator... onConditions) { - checkNatural(); - onGroup = OperatorGroup.nonGroupingClause(); - onGroup.andAll(onConditions); - return from; - } - - /** - * The USING statement of the JOIN - * - * @param columns THe columns to use - * @return The FROM that this JOIN came from - */ - @NonNull - public From using(IProperty... columns) { - checkNatural(); - Collections.addAll(using, columns); - return from; - } - - /** - * @return End this {@link Join}. Used for {@link Join.JoinType#NATURAL} - */ - public From end() { - return from; - } - - @SuppressWarnings("unchecked") - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder(); - - queryBuilder.append(type.name().replace("_", " ")).appendSpace(); - - queryBuilder.append("JOIN") - .appendSpace() - .append(alias.getFullQuery()) - .appendSpace(); - - // natural joins do no have on or using clauses. - if (!JoinType.NATURAL.equals(type)) { - if (onGroup != null) { - queryBuilder.append("ON") - .appendSpace() - .append(onGroup.getQuery()) - .appendSpace(); - } else if (!using.isEmpty()) { - queryBuilder.append("USING (") - .appendList(using) - .append(")").appendSpace(); - } - } - return queryBuilder.getQuery(); - } - - @NonNull - public Class getTable() { - return table; - } - - private void checkNatural() { - if (JoinType.NATURAL.equals(type)) { - throw new IllegalArgumentException("Cannot specify a clause for this join if its NATURAL." + - " Specifying a clause would have no effect. Call end() to continue the query."); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Method.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Method.java deleted file mode 100644 index 73b7e0172..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Method.java +++ /dev/null @@ -1,287 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.SQLiteType; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.sql.language.property.Property; -import com.raizlabs.android.dbflow.sql.language.property.PropertyFactory; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents SQLite methods on columns. These act as {@link Property} so we can use them in complex - * scenarios. - */ -public class Method extends Property { - - /** - * @param properties Set of properties that the method acts on. - * @return The average value of all properties within this group. The result is always a float from this statement - * as long as there is at least one non-NULL input. The result may be NULL if there are no non-NULL columns. - */ - @NonNull - public static Method avg(IProperty... properties) { - return new Method("AVG", properties); - } - - /** - * @param properties Set of properties that the method acts on. - * @return A count of the number of times that specified properties are not NULL in a group. Leaving - * the properties empty returns COUNT(*), which is the total number of rows in the query. - */ - @NonNull - public static Method count(IProperty... properties) { - return new Method("COUNT", properties); - } - - /** - * @param properties Set of properties that the method acts on. - * @return A string which is the concatenation of all non-NULL values of the properties. - */ - @NonNull - public static Method group_concat(IProperty... properties) { - return new Method("GROUP_CONCAT", properties); - } - - /** - * @param properties Set of properties that the method acts on. - * @return The method that represents the max of the specified columns/properties. - */ - @NonNull - public static Method max(IProperty... properties) { - return new Method("MAX", properties); - } - - /** - * @param properties Set of properties that the method acts on. - * @return The method that represents the min of the specified columns/properties. - */ - @NonNull - public static Method min(IProperty... properties) { - return new Method("MIN", properties); - } - - /** - * @param properties Set of properties that the method acts on. - * @return The method that represents the sum of the specified columns/properties. - */ - @NonNull - public static Method sum(IProperty... properties) { - return new Method("SUM", properties); - } - - /** - * @param properties Set of properties that the method acts on. - * @return The method that represents the total of the specified columns/properties. - */ - @NonNull - public static Method total(IProperty... properties) { - return new Method("TOTAL", properties); - } - - /** - * @param property The property to cast. - * @return A new CAST object. To complete use the {@link Cast#as(SQLiteType)} method. - */ - @NonNull - public static Cast cast(@NonNull IProperty property) { - return new Cast(property); - } - - @NonNull - public static Method replace(@NonNull IProperty property, String findString, String replacement) { - return new Method("REPLACE", property, PropertyFactory.from(findString), PropertyFactory.from(replacement)); - } - - /** - * SQLite standard "strftime()" method. See SQLite documentation on this method. - */ - public static Method strftime(@NonNull String formatString, - @NonNull String timeString, String... modifiers) { - List propertyList = new ArrayList<>(); - propertyList.add(PropertyFactory.from(formatString)); - propertyList.add(PropertyFactory.from(timeString)); - for (String modifier : modifiers) { - propertyList.add(PropertyFactory.from(modifier)); - } - return new Method("strftime", propertyList.toArray(new IProperty[propertyList.size()])); - } - - /** - * Sqlite "datetime" method. See SQLite documentation on this method. - */ - public static Method datetime(long timeStamp, String... modifiers) { - List propertyList = new ArrayList<>(); - propertyList.add(PropertyFactory.from(timeStamp)); - for (String modifier : modifiers) { - propertyList.add(PropertyFactory.from(modifier)); - } - return new Method("datetime", propertyList.toArray(new IProperty[propertyList.size()])); - } - - /** - * Sqlite "date" method. See SQLite documentation on this method. - */ - public static Method date(@NonNull String timeString, - String... modifiers) { - List propertyList = new ArrayList<>(); - propertyList.add(PropertyFactory.from(timeString)); - for (String modifier : modifiers) { - propertyList.add(PropertyFactory.from(modifier)); - } - return new Method("date", propertyList.toArray(new IProperty[propertyList.size()])); - } - - /** - * @return Constructs using the "IFNULL" method in SQLite. It will pick the first non null - * value and set that. If both are NULL then it will use NULL. - */ - public static Method ifNull(@NonNull IProperty first, - @NonNull IProperty secondIfFirstNull) { - return new Method("IFNULL", first, secondIfFirstNull); - } - - /** - * @return Constructs using the "NULLIF" method in SQLite. If both expressions are equal, then - * NULL is set into the DB. - */ - public static Method nullIf(@NonNull IProperty first, - @NonNull IProperty second) { - return new Method("NULLIF", first, second); - } - - private final List propertyList = new ArrayList<>(); - private List operationsList = new ArrayList<>(); - private final IProperty methodProperty; - - public Method(IProperty... properties) { - this(null, properties); - } - - @SuppressWarnings("unchecked") - public Method(String methodName, IProperty... properties) { - super(null, (String) null); - - methodProperty = new Property<>(null, NameAlias.rawBuilder(methodName).build()); - - if (properties.length == 0) { - propertyList.add(Property.ALL_PROPERTY); - } else { - for (IProperty property : properties) { - addProperty(property); - } - } - } - - @NonNull - @Override - public Method plus(@NonNull IProperty property) { - return append(property, " " + Operator.Operation.PLUS); - } - - @NonNull - @Override - public Method minus(@NonNull IProperty property) { - return append(property, " " + Operator.Operation.MINUS); - } - - @NonNull - @Override - public Property div(@NonNull IProperty property) { - return append(property, " " + Operator.Operation.DIVISION); - } - - @Override - public Property times(@NonNull IProperty property) { - return append(property, " " + Operator.Operation.MULTIPLY); - } - - @NonNull - @Override - public Property rem(@NonNull IProperty property) { - return append(property, " " + Operator.Operation.MOD); - } - - /** - * Allows adding a property to the {@link Method}. Will remove the {@link Property#ALL_PROPERTY} - * if it exists as first item. - * - * @param property The property to add. - */ - public Method addProperty(@NonNull IProperty property) { - return append(property, ","); - } - - /** - * Appends a property with the specified operation that separates it. The operation will appear before - * the property specified. - */ - public Method append(IProperty property, String operation) { - // remove all property since its not needed when we specify a property. - if (propertyList.size() == 1 && propertyList.get(0) == Property.ALL_PROPERTY) { - propertyList.remove(0); - } - propertyList.add(property); - operationsList.add(operation); - return this; - } - - @NonNull - protected List getPropertyList() { - return propertyList; - } - - @NonNull - @Override - public NameAlias getNameAlias() { - if (nameAlias == null) { - String query = methodProperty.getQuery(); - if (query == null) { - query = ""; - } - query += "("; - List propertyList = getPropertyList(); - for (int i = 0; i < propertyList.size(); i++) { - IProperty property = propertyList.get(i); - if (i > 0) { - query += operationsList.get(i) + " "; - } - query += property.toString(); - - } - query += ")"; - nameAlias = NameAlias.rawBuilder(query) - .build(); - } - return nameAlias; - } - - /** - * Represents the SQLite CAST operator. - */ - public static class Cast { - - private final IProperty property; - - private Cast(@NonNull IProperty property) { - this.property = property; - } - - /** - * @param sqLiteType The type of column to cast it to. - * @return A new {@link Method} that represents the statement. - */ - public IProperty as(SQLiteType sqLiteType) { - //noinspection unchecked - IProperty newProperty = new Property(property.getTable(), - property.getNameAlias() - .newBuilder() - .shouldAddIdentifierToAliasName(false) - .as(sqLiteType.name()) - .build()); - return new Method("CAST", newProperty); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/NameAlias.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/NameAlias.java deleted file mode 100644 index c45b5e4da..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/NameAlias.java +++ /dev/null @@ -1,308 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.StringUtils; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; - -/** - * Description: Rewritten from the ground up, this class makes it easier to build an alias. - */ -public class NameAlias implements Query { - - /** - * Combines any number of names into a single {@link NameAlias} separated by some operation. - * - * @param operation The operation to separate into. - * @param names The names to join. - * @return The new namealias object. - */ - @NonNull - public static NameAlias joinNames(@NonNull String operation, String... names) { - String newName = ""; - for (int i = 0; i < names.length; i++) { - if (i > 0) { - newName += " " + operation + " "; - } - newName += names[i]; - } - return rawBuilder(newName).build(); - } - - @NonNull - public static Builder builder(String name) { - return new Builder(name); - } - - /** - * @param name The raw name of this alias. - * @return A new instance without adding identifier `` to any part of the query. - */ - @NonNull - public static Builder rawBuilder(String name) { - return new Builder(name) - .shouldStripIdentifier(false) - .shouldAddIdentifierToName(false); - } - - @NonNull - public static NameAlias of(String name) { - return NameAlias.builder(name).build(); - } - - @NonNull - public static NameAlias of(String name, String aliasName) { - return NameAlias.builder(name).as(aliasName).build(); - } - - @NonNull - public static NameAlias ofTable(String tableName, String name) { - return NameAlias.builder(name).withTable(tableName).build(); - } - - private final String name; - private final String aliasName; - private final String tableName; - private final String keyword; - private final boolean shouldStripIdentifier; - private final boolean shouldStripAliasName; - private final boolean shouldAddIdentifierToQuery; - private final boolean shouldAddIdentifierToAliasName; - - private NameAlias(Builder builder) { - if (builder.shouldStripIdentifier) { - name = QueryBuilder.stripQuotes(builder.name); - } else { - name = builder.name; - } - keyword = builder.keyword; - if (builder.shouldStripAliasName) { - aliasName = QueryBuilder.stripQuotes(builder.aliasName); - } else { - aliasName = builder.aliasName; - } - if (StringUtils.isNotNullOrEmpty(builder.tableName)) { - tableName = QueryBuilder.quoteIfNeeded(builder.tableName); - } else { - tableName = null; - } - shouldStripIdentifier = builder.shouldStripIdentifier; - shouldStripAliasName = builder.shouldStripAliasName; - shouldAddIdentifierToQuery = builder.shouldAddIdentifierToQuery; - shouldAddIdentifierToAliasName = builder.shouldAddIdentifierToAliasName; - } - - /** - * @return The real column name. - */ - public String name() { - return (StringUtils.isNotNullOrEmpty(name) && shouldAddIdentifierToQuery) ? - QueryBuilder.quoteIfNeeded(name) : name; - } - - /** - * @return The name, stripped from identifier syntax completely. - */ - public String nameRaw() { - return shouldStripIdentifier ? name : QueryBuilder.stripQuotes(name); - } - - /** - * @return The name used as part of the AS query. - */ - public String aliasName() { - return (StringUtils.isNotNullOrEmpty(aliasName) && shouldAddIdentifierToAliasName) ? - QueryBuilder.quoteIfNeeded(aliasName) : aliasName; - } - - /** - * @return The alias name, stripped from identifier syntax completely. - */ - public String aliasNameRaw() { - return shouldStripAliasName ? aliasName : QueryBuilder.stripQuotes(aliasName); - } - - /** - * @return the table name of this query, if specified. - */ - public String tableName() { - return tableName; - } - - /** - * @return The keyword that prefixes this alias. - */ - public String keyword() { - return keyword; - } - - /** - * @return true if the name was stripped from identifier, false if not. - */ - public boolean shouldStripIdentifier() { - return shouldStripIdentifier; - } - - /** - * @return true if the alias was stripped from identifier, false if not. - */ - public boolean shouldStripAliasName() { - return shouldStripAliasName; - } - - /** - * @return The `{tableName}`.`{name}`. If {@link #tableName()} specified. - */ - public String fullName() { - return (StringUtils.isNotNullOrEmpty(tableName) ? (tableName() + ".") : "") + name(); - } - - /** - * @return The name used in queries. If an alias is specified, use that, otherwise use the name - * of the property with a table name (if specified). - */ - @Override - public String getQuery() { - if (StringUtils.isNotNullOrEmpty(aliasName)) { - return aliasName(); - } else if (StringUtils.isNotNullOrEmpty(name)) { - return fullName(); - } else { - return ""; - } - } - - /** - * @return The value used as a key. Uses either the {@link #aliasNameRaw()} - * or the {@link #nameRaw()}, depending on what's specified. - */ - public String getNameAsKey() { - if (StringUtils.isNotNullOrEmpty(aliasName)) { - return aliasNameRaw(); - } else { - return nameRaw(); - } - } - - @Override - public String toString() { - return getFullQuery(); - } - - /** - * @return The full query that represents itself with `{tableName}`.`{name}` AS `{aliasName}` - */ - public String getFullQuery() { - String query = fullName(); - if (StringUtils.isNotNullOrEmpty(aliasName)) { - query += " AS " + aliasName(); - } - if (StringUtils.isNotNullOrEmpty(keyword)) { - query = keyword + " " + query; - } - return query; - } - - /** - * @return Constructs a builder as a new instance that can be modified without fear. - */ - public Builder newBuilder() { - return new Builder(name) - .keyword(keyword) - .as(aliasName) - .shouldStripAliasName(shouldStripAliasName) - .shouldStripIdentifier(shouldStripIdentifier) - .shouldAddIdentifierToName(shouldAddIdentifierToQuery) - .shouldAddIdentifierToAliasName(shouldAddIdentifierToAliasName) - .withTable(tableName); - } - - - public static class Builder { - - private final String name; - private String aliasName; - private String tableName; - private boolean shouldStripIdentifier = true; - private boolean shouldStripAliasName = true; - private boolean shouldAddIdentifierToQuery = true; - private boolean shouldAddIdentifierToAliasName = true; - private String keyword; - - public Builder(String name) { - this.name = name; - } - - /** - * Appends a DISTINCT that prefixes this alias class. - */ - public Builder distinct() { - return keyword("DISTINCT"); - } - - /** - * Appends a keyword that prefixes this alias class. - */ - public Builder keyword(String keyword) { - this.keyword = keyword; - return this; - } - - /** - * Provide an alias that is used `{name}` AS `{aliasName}` - */ - public Builder as(String aliasName) { - this.aliasName = aliasName; - return this; - } - - /** - * Provide a table-name prefix as such: `{tableName}`.`{name}` - */ - public Builder withTable(String tableName) { - this.tableName = tableName; - return this; - } - - /** - * @param shouldStripIdentifier If true, we normalize the identifier {@link #name} from any - * ticks around the name. If false, we leave it as such. - */ - public Builder shouldStripIdentifier(boolean shouldStripIdentifier) { - this.shouldStripIdentifier = shouldStripIdentifier; - return this; - } - - /** - * @param shouldStripAliasName If true, we normalize the identifier {@link #aliasName} from any - * ticks around the name. If false, we leave it as such. - */ - public Builder shouldStripAliasName(boolean shouldStripAliasName) { - this.shouldStripAliasName = shouldStripAliasName; - return this; - } - - /** - * @param shouldAddIdentifierToName If true (default), we add the identifier to the name: `{name}` - */ - public Builder shouldAddIdentifierToName(boolean shouldAddIdentifierToName) { - this.shouldAddIdentifierToQuery = shouldAddIdentifierToName; - return this; - } - - /** - * @param shouldAddIdentifierToAliasName If true (default), we add an identifier to the alias - * name. `{aliasName}` - */ - public Builder shouldAddIdentifierToAliasName(boolean shouldAddIdentifierToAliasName) { - this.shouldAddIdentifierToAliasName = shouldAddIdentifierToAliasName; - return this; - } - - public NameAlias build() { - return new NameAlias(this); - } - - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Operator.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Operator.java deleted file mode 100644 index 5fc78e755..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Operator.java +++ /dev/null @@ -1,867 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.Collate; -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.converter.TypeConverter; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.Property; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Description: The class that contains a column name, Operator, and value. - * This class is mostly reserved for internal use at this point. Using this class directly should be avoided - * and use the generated {@link Property} instead. - */ -public class Operator extends BaseOperator implements IOperator { - - - private TypeConverter typeConverter; - private boolean convertToDB; - - public static String convertValueToString(Object value) { - return BaseOperator.convertValueToString(value, false); - } - - @NonNull - public static Operator op(NameAlias column) { - return new Operator<>(column); - } - - @NonNull - public static Operator op(NameAlias alias, TypeConverter typeConverter, boolean convertToDB) { - return new Operator<>(alias, typeConverter, convertToDB); - } - - /** - * Creates a new instance - * - * @param nameAlias The name of the column in the DB - */ - Operator(NameAlias nameAlias) { - super(nameAlias); - } - - Operator(NameAlias alias, TypeConverter typeConverter, boolean convertToDB) { - super(alias); - this.typeConverter = typeConverter; - this.convertToDB = convertToDB; - } - - Operator(Operator operator) { - super(operator.nameAlias); - this.typeConverter = operator.typeConverter; - this.convertToDB = operator.convertToDB; - this.value = operator.value; - } - - @Override - public void appendConditionToQuery(@NonNull QueryBuilder queryBuilder) { - queryBuilder.append(columnName()).append(operation()); - - // Do not use value for certain operators - // If is raw, we do not want to convert the value to a string. - if (isValueSet) { - queryBuilder.append(convertObjectToString(value(), true)); - } - - if (postArgument() != null) { - queryBuilder.appendSpace().append(postArgument()); - } - } - - @NonNull - @Override - public Operator is(@Nullable T value) { - operation = Operation.EQUALS; - return value(value); - } - - @NonNull - @Override - public Operator eq(@Nullable T value) { - return is(value); - } - - @NonNull - @Override - public Operator isNot(@Nullable T value) { - operation = Operation.NOT_EQUALS; - return value(value); - } - - @NonNull - @Override - public Operator notEq(@Nullable T value) { - return isNot(value); - } - - /** - * Uses the LIKE operation. Case insensitive comparisons. - * - * @param value Uses sqlite LIKE regex to match rows. - * It must be a string to escape it properly. - * There are two wildcards: % and _ - * % represents [0,many) numbers or characters. - * The _ represents a single number or character. - * @return This condition - */ - @NonNull - @Override - public Operator like(@NonNull String value) { - operation = String.format(" %1s ", Operation.LIKE); - return value(value); - } - - /** - * Uses the NOT LIKE operation. Case insensitive comparisons. - * - * @param value Uses sqlite LIKE regex to inversely match rows. - * It must be a string to escape it properly. - * There are two wildcards: % and _ - * % represents [0,many) numbers or characters. - * The _ represents a single number or character. - * @return This condition - */ - @NonNull - @Override - public Operator notLike(@NonNull String value) { - operation = String.format(" %1s ", Operation.NOT_LIKE); - return value(value); - } - - /** - * Uses the GLOB operation. Similar to LIKE except it uses case sensitive comparisons. - * - * @param value Uses sqlite GLOB regex to match rows. - * It must be a string to escape it properly. - * There are two wildcards: * and ? - * * represents [0,many) numbers or characters. - * The ? represents a single number or character - * @return This condition - */ - @NonNull - @Override - public Operator glob(@NonNull String value) { - operation = String.format(" %1s ", Operation.GLOB); - return value(value); - } - - /** - * The value of the parameter - * - * @param value The value of the column in the DB - * @return This condition - */ - public Operator value(@Nullable Object value) { - this.value = value; - isValueSet = true; - return this; - } - - @NonNull - @Override - public Operator greaterThan(@NonNull T value) { - operation = Operation.GREATER_THAN; - return value(value); - } - - @NonNull - @Override - public Operator greaterThanOrEq(@NonNull T value) { - operation = Operation.GREATER_THAN_OR_EQUALS; - return value(value); - } - - @NonNull - @Override - public Operator lessThan(@NonNull T value) { - operation = Operation.LESS_THAN; - return value(value); - } - - @NonNull - @Override - public Operator lessThanOrEq(@NonNull T value) { - operation = Operation.LESS_THAN_OR_EQUALS; - return value(value); - } - - @NonNull - @Override - public Operator plus(@NonNull T value) { - return assignValueOp(value, Operation.PLUS); - } - - @NonNull - @Override - public Operator minus(@NonNull T value) { - return assignValueOp(value, Operation.MINUS); - } - - @NonNull - @Override - public Operator div(@NonNull T value) { - return assignValueOp(value, Operation.DIVISION); - } - - @Override - public Operator times(@NonNull T value) { - return assignValueOp(value, Operation.MULTIPLY); - } - - @NonNull - @Override - public Operator rem(@NonNull T value) { - return assignValueOp(value, Operation.MOD); - } - - /** - * Add a custom operation to this argument - * - * @param operation The SQLite operator - * @return This condition - */ - @NonNull - public Operator operation(String operation) { - this.operation = operation; - return this; - } - - /** - * Adds a COLLATE to the end of this condition - * - * @param collation The SQLite collate function - * @return This condition. - */ - @NonNull - public Operator collate(@NonNull String collation) { - postArg = "COLLATE " + collation; - return this; - } - - /** - * Adds a COLLATE to the end of this condition using the {@link com.raizlabs.android.dbflow.annotation.Collate} enum. - * - * @param collation The SQLite collate function - * @return This condition. - */ - @NonNull - public Operator collate(@NonNull Collate collation) { - if (collation.equals(Collate.NONE)) { - postArg = null; - } else { - collate(collation.name()); - } - - return this; - } - - /** - * Appends an optional SQL string to the end of this condition - */ - @NonNull - public Operator postfix(@NonNull String postfix) { - postArg = postfix; - return this; - } - - @NonNull - @Override - public Operator isNull() { - operation = String.format(" %1s ", Operation.IS_NULL); - return this; - } - - @NonNull - @Override - public Operator isNotNull() { - operation = String.format(" %1s ", Operation.IS_NOT_NULL); - return this; - } - - /** - * Optional separator when chaining this Operator within a {@link OperatorGroup} - * - * @param separator The separator to use - * @return This instance - */ - @NonNull - @Override - public Operator separator(@NonNull String separator) { - this.separator = separator; - return this; - } - - @NonNull - @Override - public Operator is(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.EQUALS); - } - - @NonNull - @Override - public Operator eq(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.EQUALS); - } - - @NonNull - @Override - public Operator isNot(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.NOT_EQUALS); - } - - @NonNull - @Override - public Operator notEq(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.NOT_EQUALS); - } - - @NonNull - @Override - public Operator like(@NonNull IConditional conditional) { - return like(conditional.getQuery()); - } - - @NonNull - @Override - public Operator glob(@NonNull IConditional conditional) { - return glob(conditional.getQuery()); - } - - @NonNull - @Override - public Operator greaterThan(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.GREATER_THAN); - } - - @NonNull - @Override - public Operator greaterThanOrEq(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.GREATER_THAN_OR_EQUALS); - } - - @NonNull - @Override - public Operator lessThan(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.LESS_THAN); - } - - @NonNull - @Override - public Operator lessThanOrEq(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.LESS_THAN_OR_EQUALS); - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public Between between(@NonNull IConditional conditional) { - return new Between(this, conditional); - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public In in(@NonNull IConditional firstConditional, @NonNull IConditional... conditionals) { - return new In(this, firstConditional, true, conditionals); - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public In notIn(@NonNull IConditional firstConditional, @NonNull IConditional... conditionals) { - return new In(this, firstConditional, false, conditionals); - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public In notIn(@NonNull BaseModelQueriable firstBaseModelQueriable, @NonNull BaseModelQueriable[] baseModelQueriables) { - return new In(this, firstBaseModelQueriable, false, (Object[]) baseModelQueriables); - } - - @NonNull - @Override - public Operator is(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.EQUALS); - } - - @NonNull - @Override - public Operator eq(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.EQUALS); - } - - @NonNull - @Override - public Operator isNot(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.NOT_EQUALS); - } - - @NonNull - @Override - public Operator notEq(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.NOT_EQUALS); - } - - @NonNull - @Override - public Operator like(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.LIKE); - } - - @NonNull - @Override - public Operator notLike(@NonNull IConditional conditional) { - return assignValueOp(conditional, Operation.NOT_LIKE); - } - - @NonNull - @Override - public Operator notLike(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.NOT_LIKE); - } - - @NonNull - @Override - public Operator glob(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.GLOB); - } - - @NonNull - @Override - public Operator greaterThan(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.GREATER_THAN); - } - - @NonNull - @Override - public Operator greaterThanOrEq(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.GREATER_THAN_OR_EQUALS); - } - - @NonNull - @Override - public Operator lessThan(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.LESS_THAN); - } - - @NonNull - @Override - public Operator lessThanOrEq(@NonNull BaseModelQueriable baseModelQueriable) { - return assignValueOp(baseModelQueriable, Operation.LESS_THAN_OR_EQUALS); - } - - @NonNull - public Operator plus(IConditional value) { - return assignValueOp(value, Operation.PLUS); - } - - @NonNull - public Operator minus(IConditional value) { - return assignValueOp(value, Operation.MINUS); - } - - @NonNull - public Operator div(IConditional value) { - return assignValueOp(value, Operation.DIVISION); - } - - @NonNull - public Operator times(IConditional value) { - return assignValueOp(value, Operation.MULTIPLY); - } - - @NonNull - public Operator rem(IConditional value) { - return assignValueOp(value, Operation.MOD); - } - - @NonNull - @Override - public Operator plus(@NonNull BaseModelQueriable value) { - return assignValueOp(value, Operation.PLUS); - } - - @NonNull - @Override - public Operator minus(@NonNull BaseModelQueriable value) { - return assignValueOp(value, Operation.MINUS); - } - - @NonNull - @Override - public Operator div(@NonNull BaseModelQueriable value) { - return assignValueOp(value, Operation.DIVISION); - } - - @NonNull - @Override - public Operator times(@NonNull BaseModelQueriable value) { - return assignValueOp(value, Operation.MULTIPLY); - } - - @NonNull - @Override - public Operator rem(@NonNull BaseModelQueriable value) { - return assignValueOp(value, Operation.MOD); - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public Between between(@NonNull BaseModelQueriable baseModelQueriable) { - return new Between(this, baseModelQueriable); - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public In in(@NonNull BaseModelQueriable firstBaseModelQueriable, @NonNull BaseModelQueriable... baseModelQueriables) { - return new In(this, firstBaseModelQueriable, true, baseModelQueriables); - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder(); - appendConditionToQuery(queryBuilder); - return queryBuilder.getQuery(); - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public Operator concatenate(@Nullable Object value) { - operation = new QueryBuilder(Operation.EQUALS).append(columnName()).toString(); - - TypeConverter typeConverter = this.typeConverter; - if (typeConverter == null && value != null) { - typeConverter = FlowManager.getTypeConverterForClass(value.getClass()); - } - if (typeConverter != null && convertToDB) { - value = typeConverter.getDBValue(value); - } - if (value instanceof String || value instanceof IOperator - || value instanceof Character) { - operation = String.format("%1s %1s ", operation, Operation.CONCATENATE); - } else if (value instanceof Number) { - operation = String.format("%1s %1s ", operation, Operation.PLUS); - } else { - throw new IllegalArgumentException( - String.format("Cannot concatenate the %1s", value != null ? value.getClass() : "null")); - } - this.value = value; - isValueSet = true; - return this; - } - - @NonNull - @Override - public Operator concatenate(@NonNull IConditional conditional) { - return concatenate((Object) conditional); - } - - /** - * Turns this condition into a SQL BETWEEN operation - * - * @param value The value of the first argument of the BETWEEN clause - * @return Between operator - */ - @NonNull - @Override - public Between between(@NonNull T value) { - return new Between<>(this, value); - } - - @NonNull - @SafeVarargs - @Override - public final In in(@NonNull T firstArgument, T... arguments) { - return new In<>(this, firstArgument, true, arguments); - } - - @NonNull - @SafeVarargs - @Override - public final In notIn(@NonNull T firstArgument, T... arguments) { - return new In<>(this, firstArgument, false, arguments); - } - - @NonNull - @Override - public In in(@NonNull Collection values) { - return new In<>(this, values, true); - } - - @NonNull - @Override - public In notIn(@NonNull Collection values) { - return new In<>(this, values, false); - } - - @SuppressWarnings("unchecked") - @Override - public String convertObjectToString(Object object, boolean appendInnerParenthesis) { - if (typeConverter != null) { - Object converted = object; - try { - converted = convertToDB ? typeConverter.getDBValue(object) : object; - } catch (ClassCastException c) { - // if object type is not valid converted type, just use type as is here. - FlowLog.log(FlowLog.Level.I, "Value passed to operation is not valid type for TypeConverter in the column. " + - "Preserving value " + object + " to be used as is."); - } - return BaseOperator.convertValueToString(converted, appendInnerParenthesis, false); - } else { - return super.convertObjectToString(object, appendInnerParenthesis); - } - } - - private Operator assignValueOp(Object value, String operation) { - this.operation = operation; - return value(value); - } - - /** - * Static constants that define condition operations - */ - public static class Operation { - - /** - * Equals comparison - */ - public static final String EQUALS = "="; - - /** - * Not-equals comparison - */ - public static final String NOT_EQUALS = "!="; - - /** - * String concatenation - */ - public static final String CONCATENATE = "||"; - - /** - * Number addition - */ - public static final String PLUS = "+"; - - /** - * Number subtraction - */ - public static final String MINUS = "-"; - - public static final String DIVISION = "/"; - - public static final String MULTIPLY = "*"; - - public static final String MOD = "%"; - - /** - * If something is LIKE another (a case insensitive search). - * There are two wildcards: % and _ - * % represents [0,many) numbers or characters. - * The _ represents a single number or character. - */ - public static final String LIKE = "LIKE"; - - /** - * If something is NOT LIKE another (a case insensitive search). - * There are two wildcards: % and _ - * % represents [0,many) numbers or characters. - * The _ represents a single number or character. - */ - public static final String NOT_LIKE = "NOT LIKE"; - - /** - * If something is case sensitive like another. - * It must be a string to escape it properly. - * There are two wildcards: * and ? - * * represents [0,many) numbers or characters. - * The ? represents a single number or character - */ - public static final String GLOB = "GLOB"; - - /** - * Greater than some value comparison - */ - public static final String GREATER_THAN = ">"; - - /** - * Greater than or equals to some value comparison - */ - public static final String GREATER_THAN_OR_EQUALS = ">="; - - /** - * Less than some value comparison - */ - public static final String LESS_THAN = "<"; - - /** - * Less than or equals to some value comparison - */ - public static final String LESS_THAN_OR_EQUALS = "<="; - - /** - * Between comparison. A simplification of X<Y AND Y<Z to Y BETWEEN X AND Z - */ - public static final String BETWEEN = "BETWEEN"; - - /** - * AND comparison separator - */ - public static final String AND = "AND"; - - /** - * OR comparison separator - */ - public static final String OR = "OR"; - - /** - * An empty value for the condition. - */ - public static final String EMPTY_PARAM = "?"; - - /** - * Special operation that specify if the column is not null for a specified row. Use of this as - * an operator will ignore the value of the {@link Operator} for it. - */ - public static final String IS_NOT_NULL = "IS NOT NULL"; - - /** - * Special operation that specify if the column is null for a specified row. Use of this as - * an operator will ignore the value of the {@link Operator} for it. - */ - public static final String IS_NULL = "IS NULL"; - - /** - * The SQLite IN command that will select rows that are contained in a list of values. - * EX: SELECT * from Table where column IN ('first', 'second', etc) - */ - public static final String IN = "IN"; - - /** - * The reverse of the {@link #IN} command that selects rows that are not contained - * in a list of values specified. - */ - public static final String NOT_IN = "NOT IN"; - } - - /** - * The SQL BETWEEN operator that contains two values instead of the normal 1. - */ - public static class Between extends BaseOperator implements Query { - - @Nullable - private T secondValue; - - /** - * Creates a new instance - * - * @param operator - * @param value The value of the first argument of the BETWEEN clause - */ - private Between(Operator operator, T value) { - super(operator.nameAlias); - this.operation = String.format(" %1s ", Operation.BETWEEN); - this.value = value; - isValueSet = true; - this.postArg = operator.postArgument(); - } - - @NonNull - public Between and(@Nullable T secondValue) { - this.secondValue = secondValue; - return this; - } - - @Nullable - public T secondValue() { - return secondValue; - } - - @Override - public void appendConditionToQuery(@NonNull QueryBuilder queryBuilder) { - queryBuilder.append(columnName()).append(operation()) - .append(convertObjectToString(value(), true)) - .appendSpaceSeparated(Operation.AND) - .append(convertObjectToString(secondValue(), true)) - .appendSpace().appendOptional(postArgument()); - } - - @Override - public String getQuery() { - QueryBuilder builder = new QueryBuilder(); - appendConditionToQuery(builder); - return builder.getQuery(); - } - } - - /** - * The SQL IN and NOT IN operator that specifies a list of values to SELECT rows from. - * EX: SELECT * FROM myTable WHERE columnName IN ('column1','column2','etc') - */ - public static class In extends BaseOperator implements Query { - - private List inArguments = new ArrayList<>(); - - /** - * Creates a new instance - * - * @param operator The operator object to pass in. We only use the column name here. - * @param firstArgument The first value in the IN query as one is required. - * @param isIn if this is an {@link Operator.Operation#IN} - * statement or a {@link Operator.Operation#NOT_IN} - */ - @SafeVarargs - private In(Operator operator, T firstArgument, boolean isIn, T... arguments) { - super(operator.columnAlias()); - inArguments.add(firstArgument); - Collections.addAll(inArguments, arguments); - operation = String.format(" %1s ", isIn ? Operation.IN : Operation.NOT_IN); - } - - private In(Operator operator, Collection args, boolean isIn) { - super(operator.columnAlias()); - inArguments.addAll(args); - operation = String.format(" %1s ", isIn ? Operation.IN : Operation.NOT_IN); - } - - /** - * Appends another value to this In statement - * - * @param argument The non-type converted value of the object. The value will be converted - * in a {@link OperatorGroup}. - * @return - */ - @NonNull - public In and(@Nullable T argument) { - inArguments.add(argument); - return this; - } - - @Override - public void appendConditionToQuery(@NonNull QueryBuilder queryBuilder) { - queryBuilder.append(columnName()).append(operation()) - .append("(").append(OperatorGroup.joinArguments(",", inArguments, this)).append(")"); - } - - @Override - public String getQuery() { - QueryBuilder builder = new QueryBuilder(); - appendConditionToQuery(builder); - return builder.getQuery(); - } - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OperatorGroup.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OperatorGroup.java deleted file mode 100644 index 11280ecf2..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OperatorGroup.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.Operator.Operation; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -/** - * Allows combining of {@link SQLOperator} into one condition. - */ -public class OperatorGroup extends BaseOperator implements Query, Iterable { - - /** - * @return Starts an arbitrary clause of conditions to use. - */ - @NonNull - public static OperatorGroup clause() { - return new OperatorGroup(); - } - - /** - * @return Starts an arbitrary clause of conditions to use with first param as conditions separated by AND. - */ - @NonNull - public static OperatorGroup clause(SQLOperator... condition) { - return new OperatorGroup().andAll(condition); - } - - /** - * @return Starts an arbitrary clause of conditions to use, that when included in other {@link SQLOperator}, - * does not append parenthesis to group it. - */ - public static OperatorGroup nonGroupingClause() { - return new OperatorGroup().setUseParenthesis(false); - } - - /** - * @return Starts an arbitrary clause of conditions (without parenthesis) to use with first param as conditions separated by AND. - */ - public static OperatorGroup nonGroupingClause(SQLOperator... condition) { - return new OperatorGroup().setUseParenthesis(false).andAll(condition); - } - - @NonNull - private final List conditionsList = new ArrayList<>(); - - private QueryBuilder query; - private boolean isChanged; - private boolean allCommaSeparated; - private boolean useParenthesis = true; - - protected OperatorGroup(NameAlias columnName) { - super(columnName); - - // default is AND - separator = Operation.AND; - } - - protected OperatorGroup() { - this(null); - } - - /** - * Will ignore all separators for the group and make them separated by comma. This is useful - * in {@link Set} statements. - * - * @param allCommaSeparated All become comma separated. - * @return This instance. - */ - @NonNull - public OperatorGroup setAllCommaSeparated(boolean allCommaSeparated) { - this.allCommaSeparated = allCommaSeparated; - isChanged = true; - return this; - } - - /** - * Sets whether we use paranthesis when grouping this within other {@link SQLOperator}. The default - * is true, but if no conditions exist there are no paranthesis anyways. - * - * @param useParenthesis true if we use them, false if not. - */ - @NonNull - public OperatorGroup setUseParenthesis(boolean useParenthesis) { - this.useParenthesis = useParenthesis; - isChanged = true; - return this; - } - - /** - * Appends the {@link SQLOperator} with an {@link Operation#OR} - * - * @param sqlOperator The condition to append. - * @return This instance. - */ - @NonNull - public OperatorGroup or(SQLOperator sqlOperator) { - return operator(Operation.OR, sqlOperator); - } - - /** - * Appends the {@link SQLOperator} with an {@link Operation#AND} - */ - @NonNull - public OperatorGroup and(SQLOperator sqlOperator) { - return operator(Operation.AND, sqlOperator); - } - - /** - * Applies the {@link Operation#AND} to all of the passed - * {@link SQLOperator}. - */ - @NonNull - public OperatorGroup andAll(SQLOperator... sqlOperators) { - for (SQLOperator sqlOperator : sqlOperators) { - and(sqlOperator); - } - return this; - } - - /** - * Applies the {@link Operation#AND} to all of the passed - * {@link SQLOperator}. - */ - @NonNull - public OperatorGroup andAll(Collection sqlOperators) { - for (SQLOperator sqlOperator : sqlOperators) { - and(sqlOperator); - } - return this; - } - - /** - * Applies the {@link Operation#AND} to all of the passed - * {@link SQLOperator}. - */ - @NonNull - public OperatorGroup orAll(SQLOperator... sqlOperators) { - for (SQLOperator sqlOperator : sqlOperators) { - or(sqlOperator); - } - return this; - } - - /** - * Applies the {@link Operation#AND} to all of the passed - * {@link SQLOperator}. - */ - @NonNull - public OperatorGroup orAll(Collection sqlOperators) { - for (SQLOperator sqlOperator : sqlOperators) { - or(sqlOperator); - } - return this; - } - - /** - * Appends the {@link SQLOperator} with the specified operator string. - */ - @NonNull - private OperatorGroup operator(String operator, @Nullable SQLOperator sqlOperator) { - if (sqlOperator != null) { - setPreviousSeparator(operator); - conditionsList.add(sqlOperator); - isChanged = true; - } - return this; - } - - @Override - public void appendConditionToQuery(@NonNull QueryBuilder queryBuilder) { - int conditionListSize = conditionsList.size(); - if (useParenthesis && conditionListSize > 0) { - queryBuilder.append("("); - } - for (int i = 0; i < conditionListSize; i++) { - SQLOperator condition = conditionsList.get(i); - condition.appendConditionToQuery(queryBuilder); - if (!allCommaSeparated && condition.hasSeparator() && i < conditionListSize - 1) { - queryBuilder.appendSpaceSeparated(condition.separator()); - } else if (i < conditionListSize - 1) { - queryBuilder.append(", "); - } - } - if (useParenthesis && conditionListSize > 0) { - queryBuilder.append(")"); - } - } - - /** - * Sets the last condition to use the separator specified - * - * @param separator AND, OR, etc. - */ - private void setPreviousSeparator(String separator) { - if (conditionsList.size() > 0) { - // set previous to use OR separator - conditionsList.get(conditionsList.size() - 1).separator(separator); - } - } - - @Override - public String getQuery() { - if (isChanged) { - query = getQuerySafe(); - } - return query == null ? "" : query.toString(); - } - - @Override - public String toString() { - return getQuerySafe().toString(); - } - - public int size() { - return conditionsList.size(); - } - - @NonNull - public List getConditions() { - return conditionsList; - } - - @Override - public Iterator iterator() { - return conditionsList.iterator(); - } - - private QueryBuilder getQuerySafe() { - QueryBuilder query = new QueryBuilder(); - appendConditionToQuery(query); - return query; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java deleted file mode 100644 index 6b21c0f1a..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/OrderBy.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.Collate; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; - -/** - * Description: Class that represents a SQL order-by. - */ -public class OrderBy implements Query { - - public static final String ASCENDING = "ASC"; - - public static final String DESCENDING = "DESC"; - - private NameAlias column; - - private boolean isAscending; - - private Collate collation; - private String orderByString; - - @NonNull - public static OrderBy fromProperty(@NonNull IProperty property) { - return new OrderBy(property.getNameAlias()); - } - - @NonNull - public static OrderBy fromNameAlias(@NonNull NameAlias nameAlias) { - return new OrderBy(nameAlias); - } - - @NonNull - public static OrderBy fromString(@NonNull String orderByString) { - return new OrderBy(orderByString); - } - - OrderBy(NameAlias column) { - this.column = column; - } - - OrderBy(NameAlias column, boolean isAscending) { - this(column); - this.isAscending = isAscending; - } - - OrderBy(String orderByString) { - this.orderByString = orderByString; - } - - @NonNull - public OrderBy ascending() { - isAscending = true; - return this; - } - - @NonNull - public OrderBy descending() { - isAscending = false; - return this; - } - - @NonNull - public OrderBy collate(Collate collate) { - this.collation = collate; - return this; - } - - @Override - public String getQuery() { - if (orderByString == null) { - StringBuilder query = new StringBuilder() - .append(column) - .append(" "); - if (collation != null) { - query.append("COLLATE").append(" ").append(collation).append(" "); - } - query.append(isAscending ? ASCENDING : DESCENDING); - return query.toString(); - } else { - return orderByString; - } - } - - @Override - public String toString() { - return getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/SQLite.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/SQLite.java deleted file mode 100644 index e0be50892..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/SQLite.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.sql.language.property.Property; -import com.raizlabs.android.dbflow.structure.Model; - -/** - * Description: The main entry point into SQLite queries. - */ -public class SQLite { - - /** - * @param properties The properties/columns to SELECT. - * @return A beginning of the SELECT statement. - */ - @NonNull - public static Select select(IProperty... properties) { - return new Select(properties); - } - - /** - * Starts a new SELECT COUNT(property1, property2, propertyn) (if properties specified) or - * SELECT COUNT(*). - * - * @param properties Optional, if specified returns the count of non-null ROWs from a specific single/group of columns. - * @return A new select statement SELECT COUNT(expression) - */ - @NonNull - public static Select selectCountOf(IProperty... properties) { - return new Select(Method.count(properties)); - } - - /** - * @param table The tablet to update. - * @param The class that implements {@link Model}. - * @return A new UPDATE statement. - */ - @NonNull - public static Update update(@NonNull Class table) { - return new Update<>(table); - } - - /** - * @param table The table to insert. - * @param The class that implements {@link Model}. - * @return A new INSERT statement. - */ - @NonNull - public static Insert insert(@NonNull Class table) { - return new Insert<>(table); - } - - /** - * @return Begins a DELETE statement. - */ - @NonNull - public static Delete delete() { - return new Delete(); - } - - /** - * Starts a DELETE statement on the specified table. - * - * @param table The table to delete from. - * @param The class that implements {@link Model}. - * @return A {@link From} with specified DELETE on table. - */ - @NonNull - public static From delete(@NonNull Class table) { - return delete().from(table); - } - - /** - * Starts an INDEX statement on specified table. - * - * @param name The name of the index. - * @param The class that implements {@link Model}. - * @return A new INDEX statement. - */ - @NonNull - public static Index index(@NonNull String name) { - return new Index<>(name); - } - - /** - * Starts a TRIGGER statement. - * - * @param name The name of the trigger. - * @return A new TRIGGER statement. - */ - @NonNull - public static Trigger createTrigger(@NonNull String name) { - return Trigger.create(name); - } - - /** - * Starts a CASE statement. - * - * @param operator The condition to check for in the WHEN. - * @return A new {@link CaseCondition}. - */ - @NonNull - public static CaseCondition caseWhen(@NonNull SQLOperator operator) { - return new Case().when(operator); - } - - /** - * Starts an efficient CASE statement. The value passed here is only evaulated once. A non-efficient - * case statement will evaluate all of its {@link SQLOperator}. - * - * @param caseColumn The value - */ - @NonNull - public static Case _case(@NonNull Property caseColumn) { - return new Case<>(caseColumn); - } - - /** - * Starts an efficient CASE statement. The value passed here is only evaulated once. A non-efficient - * case statement will evaluate all of its {@link SQLOperator}. - * - * @param caseColumn The value - */ - @NonNull - public static Case _case(@NonNull IProperty caseColumn) { - return new Case<>(caseColumn); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Select.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Select.java deleted file mode 100644 index 76986122b..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Select.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.sql.language.property.Property; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Description: A SQL SELECT statement generator. It generates the SELECT part of the statement. - */ -public class Select implements Query { - - /** - * Default does not include the qualifier - */ - public static final int NONE = -1; - /** - * The select qualifier to append to the SELECT statement - */ - private int mSelectQualifier = NONE; - /** - * SELECT DISTINCT call - */ - public static final int DISTINCT = 0; - /** - * SELECT ALL call - */ - public static final int ALL = 1; - - private final List propertyList = new ArrayList<>(); - - /** - * Creates this instance with the specified columns from the specified {@link com.raizlabs.android.dbflow.config.FlowManager} - * - * @param properties The properties to select from. - */ - public Select(IProperty... properties) { - Collections.addAll(propertyList, properties); - - if (propertyList.isEmpty()) { - propertyList.add(Property.ALL_PROPERTY); - } - } - - /** - * Passes this statement to the {@link From} - * - * @param table The model table to run this query on - * @param The class that implements {@link com.raizlabs.android.dbflow.structure.Model} - * @return the From part of this query - */ - @NonNull - public From from(@NonNull Class table) { - return new From<>(this, table); - } - - /** - * appends {@link #DISTINCT} to the query - * - * @return - */ - @NonNull - public Select distinct() { - return selectQualifier(DISTINCT); - } - - @NonNull - public String toString() { - return getQuery(); - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder("SELECT "); - - if (mSelectQualifier != NONE) { - if (mSelectQualifier == DISTINCT) { - queryBuilder.append("DISTINCT"); - } else if (mSelectQualifier == ALL) { - queryBuilder.append("ALL"); - } - queryBuilder.appendSpace(); - } - - queryBuilder.append(QueryBuilder.join(",", propertyList)); - queryBuilder.appendSpace(); - return queryBuilder.getQuery(); - } - - - /** - * Helper method to pick the correct qualifier for a SELECT query - * - * @param qualifierInt Can be {@link #ALL}, {@link #NONE}, or {@link #DISTINCT} - * @return - */ - private Select selectQualifier(int qualifierInt) { - mSelectQualifier = qualifierInt; - return this; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Set.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Set.java deleted file mode 100644 index f685e0959..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Set.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.content.ContentValues; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.SqlUtils; -import com.raizlabs.android.dbflow.structure.BaseModel; - -/** - * Description: Used to specify the SET part of an {@link com.raizlabs.android.dbflow.sql.language.Update} query. - */ -public class Set extends BaseTransformable implements WhereBase { - - private OperatorGroup operatorGroup; - - private Query update; - - public Set(@NonNull Query update, @NonNull Class table) { - super(table); - this.update = update; - operatorGroup = OperatorGroup.nonGroupingClause().setAllCommaSeparated(true); - } - - /** - * Specifies a varg of conditions to append to this SET - * - * @param conditions The varg of conditions - * @return This instance. - */ - @NonNull - public Set conditions(SQLOperator... conditions) { - operatorGroup.andAll(conditions); - return this; - } - - @NonNull - public Set conditionValues(@NonNull ContentValues contentValues) { - SqlUtils.addContentValues(contentValues, operatorGroup); - return this; - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = - new QueryBuilder(update.getQuery()) - .append("SET ") - .append(operatorGroup.getQuery()).appendSpace(); - return queryBuilder.getQuery(); - } - - @NonNull - @Override - public Query getQueryBuilderBase() { - return update; - } - - @NonNull - @Override - public BaseModel.Action getPrimaryAction() { - return BaseModel.Action.UPDATE; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Transformable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Transformable.java deleted file mode 100644 index 298e411b1..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Transformable.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.language.property.IProperty; - -import java.util.List; - -/** - * Description: Provides a standard set of methods for ending a SQLite query method. These include - * groupby, orderby, having, limit and offset. - */ -public interface Transformable { - - @NonNull - Where groupBy(NameAlias... nameAliases); - - @NonNull - Where groupBy(IProperty... properties); - - @NonNull - Where orderBy(@NonNull NameAlias nameAlias, boolean ascending); - - @NonNull - Where orderBy(@NonNull IProperty property, boolean ascending); - - @NonNull - Where orderBy(@NonNull OrderBy orderBy); - - @NonNull - Where limit(int count); - - @NonNull - Where offset(int offset); - - @NonNull - Where having(SQLOperator... conditions); - - @NonNull - Where orderByAll(@NonNull List orderBies); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Trigger.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Trigger.java deleted file mode 100644 index f264fddcf..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Trigger.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; - -/** - * Description: Describes an easy way to create a SQLite TRIGGER - */ -public class Trigger implements Query { - - /** - * Specifies that we should do this TRIGGER before some event - */ - public static final String BEFORE = "BEFORE"; - - /** - * Specifies that we should do this TRIGGER after some event - */ - public static final String AFTER = "AFTER"; - - /** - * Specifies that we should do this TRIGGER instead of the specified events - */ - public static final String INSTEAD_OF = "INSTEAD OF"; - - /** - * The name in the DB - */ - final String triggerName; - - /** - * If it's {@link #BEFORE}, {@link #AFTER}, or {@link #INSTEAD_OF} - */ - String beforeOrAfter; - - boolean temporary; - - /** - * @param triggerName The name of the trigger to use. - * @return A new trigger. - */ - @NonNull - public static Trigger create(@NonNull String triggerName) { - return new Trigger(triggerName); - } - - /** - * Creates a trigger with the specified trigger name. You need to complete - * the trigger using - * - * @param triggerName What we should call this trigger - */ - private Trigger(@NonNull String triggerName) { - this.triggerName = triggerName; - } - - /** - * Sets the trigger as temporary. - */ - @NonNull - public Trigger temporary() { - this.temporary = true; - return this; - } - - /** - * Specifies AFTER eventName - */ - @NonNull - public Trigger after() { - beforeOrAfter = AFTER; - return this; - } - - /** - * Specifies BEFORE eventName - */ - @NonNull - public Trigger before() { - beforeOrAfter = BEFORE; - return this; - } - - /** - * Specifies INSTEAD OF eventName - */ - @NonNull - public Trigger insteadOf() { - beforeOrAfter = INSTEAD_OF; - return this; - } - - /** - * Starts a DELETE ON command - * - * @param onTable The table ON - */ - @NonNull - public TriggerMethod deleteOn(@NonNull Class onTable) { - return new TriggerMethod<>(this, TriggerMethod.DELETE, onTable); - } - - /** - * Starts a INSERT ON command - * - * @param onTable The table ON - */ - @NonNull - public TriggerMethod insertOn(@NonNull Class onTable) { - return new TriggerMethod<>(this, TriggerMethod.INSERT, onTable); - } - - /** - * Starts an UPDATE ON command - * - * @param onTable The table ON - * @param properties if empty, will not execute an OF command. If you specify columns, - * the UPDATE OF column1, column2,... will be used. - */ - @NonNull - public TriggerMethod updateOn(@NonNull Class onTable, IProperty... properties) { - return new TriggerMethod<>(this, TriggerMethod.UPDATE, onTable, properties); - } - - /** - * @return The name of this TRIGGER - */ - @NonNull - public String getName() { - return triggerName; - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder("CREATE "); - if (temporary) { - queryBuilder.append("TEMP "); - } - queryBuilder.append("TRIGGER IF NOT EXISTS ") - .appendQuotedIfNeeded(triggerName).appendSpace() - .appendOptional(beforeOrAfter + " "); - - return queryBuilder.getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/TriggerMethod.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/TriggerMethod.java deleted file mode 100644 index 270045074..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/TriggerMethod.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; - -/** - * Description: Describes the method that the trigger uses. - */ -public class TriggerMethod implements Query { - - public static final String DELETE = "DELETE"; - public static final String INSERT = "INSERT"; - public static final String UPDATE = "UPDATE"; - - final Trigger trigger; - private IProperty[] properties; - private final String methodName; - - /** - * The table we're operating on. - */ - Class onTable; - boolean forEachRow = false; - private SQLOperator whenCondition; - - TriggerMethod(Trigger trigger, String methodName, Class onTable, IProperty... properties) { - this.trigger = trigger; - this.methodName = methodName; - this.onTable = onTable; - if (properties != null && properties.length > 0 && properties[0] != null) { - if (!methodName.equals(UPDATE)) { - throw new IllegalArgumentException("An Trigger OF can only be used with an UPDATE method"); - } - this.properties = properties; - } - } - - @NonNull - public TriggerMethod forEachRow() { - forEachRow = true; - return this; - } - - /** - * Appends a WHEN condition after the ON name and before BEGIN...END - * - * @param condition The condition for the trigger - * @return - */ - @NonNull - public TriggerMethod when(@NonNull SQLOperator condition) { - whenCondition = condition; - return this; - } - - /** - * Specify the logic that gets executed for this trigger. Supported statements include: - * {@link Update}, INSERT, {@link Delete}, - * and {@link Select} - * - * @param triggerLogicQuery The query to run for the BEGIN..END of the trigger - * @return This trigger - */ - @NonNull - public CompletedTrigger begin(@NonNull Query triggerLogicQuery) { - return new CompletedTrigger<>(this, triggerLogicQuery); - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder - = new QueryBuilder(trigger.getQuery()) - .append(methodName); - if (properties != null && properties.length > 0) { - queryBuilder.appendSpaceSeparated("OF") - .appendArray((Object[]) properties); - } - queryBuilder.appendSpaceSeparated("ON").append(FlowManager.getTableName(onTable)); - - if (forEachRow) { - queryBuilder.appendSpaceSeparated("FOR EACH ROW"); - } - - if (whenCondition != null) { - queryBuilder.append(" WHEN "); - whenCondition.appendConditionToQuery(queryBuilder); - queryBuilder.appendSpace(); - } - - queryBuilder.appendSpace(); - - return queryBuilder.getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/UnSafeStringOperator.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/UnSafeStringOperator.java deleted file mode 100644 index f9d225000..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/UnSafeStringOperator.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.StringUtils; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; - -/** - * Description: This class will use a String to describe its condition. - * Not recommended for normal queries, but can be used as a fall-back. - */ -public class UnSafeStringOperator implements SQLOperator, Query { - - private final String conditionString; - private String separator = ""; - - public UnSafeStringOperator(String selection, String[] selectionArgs) { - String newSelection = selection; - // replace question marks in order - if (newSelection != null) { - for (String selectionArg : selectionArgs) { - newSelection = newSelection.replaceFirst("\\?", selectionArg); - } - } - this.conditionString = newSelection; - } - - @Override - public void appendConditionToQuery(@NonNull QueryBuilder queryBuilder) { - queryBuilder.append(conditionString); - } - - @NonNull - @Override - public String columnName() { - return ""; - } - - @Nullable - @Override - public String separator() { - return separator; - } - - @NonNull - @Override - public SQLOperator separator(@NonNull String separator) { - this.separator = separator; - return this; - } - - @Override - public boolean hasSeparator() { - return StringUtils.isNotNullOrEmpty(separator); - } - - @NonNull - @Override - public String operation() { - return ""; - } - - @Override - public Object value() { - return ""; - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder(); - appendConditionToQuery(queryBuilder); - return queryBuilder.getQuery(); - } -} \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Update.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Update.java deleted file mode 100644 index ea1764d3e..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Update.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.ConflictAction; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.QueryBuilder; - -/** - * Description: The SQLite UPDATE query. Will update rows in the DB. - */ -public class Update implements Query { - - /** - * The conflict action to resolve updates. - */ - private ConflictAction conflictAction = ConflictAction.NONE; - - private final Class table; - - /** - * Constructs new instace of an UPDATE query with the specified table. - * - * @param table The table to use. - */ - public Update(Class table) { - this.table = table; - } - - @NonNull - public Update conflictAction(@NonNull ConflictAction conflictAction) { - this.conflictAction = conflictAction; - return this; - } - - @NonNull - public Update or(@NonNull ConflictAction conflictAction) { - return conflictAction(conflictAction); - } - - /** - * @return This instance. - * @see ConflictAction#ROLLBACK - */ - @NonNull - public Update orRollback() { - return conflictAction(ConflictAction.ROLLBACK); - } - - /** - * @return This instance. - * @see ConflictAction#ABORT - */ - @NonNull - public Update orAbort() { - return conflictAction(ConflictAction.ABORT); - } - - /** - * @return This instance. - * @see ConflictAction#REPLACE - */ - @NonNull - public Update orReplace() { - return conflictAction(ConflictAction.REPLACE); - } - - /** - * @return This instance. - * @see ConflictAction#FAIL - */ - @NonNull - public Update orFail() { - return conflictAction(ConflictAction.FAIL); - } - - /** - * @return This instance. - * @see ConflictAction#IGNORE - */ - @NonNull - public Update orIgnore() { - return conflictAction(ConflictAction.IGNORE); - } - - /** - * Begins a SET piece of the SQL query - * - * @param conditions The array of conditions that define this SET statement - * @return A SET query piece of this statement - */ - @NonNull - public Set set(SQLOperator... conditions) { - return new Set<>(this, table).conditions(conditions); - } - - @Override - public String getQuery() { - QueryBuilder queryBuilder = new QueryBuilder("UPDATE "); - if (conflictAction != null && !conflictAction.equals(ConflictAction.NONE)) { - queryBuilder.append("OR").appendSpaceSeparated(conflictAction.name()); - } - queryBuilder.append(FlowManager.getTableName(table)).appendSpace(); - return queryBuilder.getQuery(); - } - - public Class getTable() { - return table; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java deleted file mode 100644 index 6c73082f4..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/Where.java +++ /dev/null @@ -1,264 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.database.Cursor; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.provider.ContentProvider; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Description: Defines the SQL WHERE statement of the query. - */ -public class Where extends BaseModelQueriable implements ModelQueriable, - Transformable { - - private static final int VALUE_UNSET = -1; - - /** - * The first chunk of the SQL statement before this query. - */ - private final WhereBase whereBase; - - /** - * Helps to build the where statement easily - */ - private OperatorGroup operatorGroup; - - private final List groupByList = new ArrayList<>(); - - private final List orderByList = new ArrayList<>(); - - /** - * The SQL HAVING statement - */ - private OperatorGroup havingGroup; - - private int limit = VALUE_UNSET; - private int offset = VALUE_UNSET; - - /** - * Constructs this class with the specified {@link com.raizlabs.android.dbflow.config.FlowManager} - * and {@link From} chunk - * - * @param whereBase The FROM or SET statement chunk - */ - public Where(@NonNull WhereBase whereBase, SQLOperator... conditions) { - super(whereBase.getTable()); - this.whereBase = whereBase; - operatorGroup = OperatorGroup.nonGroupingClause(); - havingGroup = OperatorGroup.nonGroupingClause(); - - operatorGroup.andAll(conditions); - } - - /** - * Joins the {@link SQLOperator} by the prefix of "AND" (unless its the first condition). - */ - @NonNull - public Where and(@NonNull SQLOperator condition) { - operatorGroup.and(condition); - return this; - } - - /** - * Joins the {@link SQLOperator} by the prefix of "OR" (unless its the first condition). - */ - @NonNull - public Where or(@NonNull SQLOperator condition) { - operatorGroup.or(condition); - return this; - } - - /** - * Joins all of the {@link SQLOperator} by the prefix of "AND" (unless its the first condition). - */ - @NonNull - public Where andAll(@NonNull List conditions) { - operatorGroup.andAll(conditions); - return this; - } - - /** - * Joins all of the {@link SQLOperator} by the prefix of "AND" (unless its the first condition). - */ - @NonNull - public Where andAll(SQLOperator... conditions) { - operatorGroup.andAll(conditions); - return this; - } - - @NonNull - public Where groupBy(NameAlias... columns) { - Collections.addAll(groupByList, columns); - return this; - } - - @NonNull - public Where groupBy(IProperty... properties) { - for (IProperty property : properties) { - groupByList.add(property.getNameAlias()); - } - return this; - } - - /** - * Defines a SQL HAVING statement without the HAVING. - * - * @param conditions The array of {@link SQLOperator} - * @return - */ - @NonNull - public Where having(SQLOperator... conditions) { - havingGroup.andAll(conditions); - return this; - } - - @NonNull - public Where orderBy(@NonNull NameAlias nameAlias, boolean ascending) { - orderByList.add(new OrderBy(nameAlias, ascending)); - return this; - } - - @NonNull - public Where orderBy(@NonNull IProperty property, boolean ascending) { - orderByList.add(new OrderBy(property.getNameAlias(), ascending)); - return this; - } - - @NonNull - public Where orderBy(@NonNull OrderBy orderBy) { - orderByList.add(orderBy); - return this; - } - - /** - * For use in {@link ContentProvider} generation. Appends all ORDER BY here. - * - * @param orderBies The order by. - * @return this instance. - */ - @NonNull - public Where orderByAll(@NonNull List orderBies) { - orderByList.addAll(orderBies); - return this; - } - - @NonNull - public Where limit(int count) { - this.limit = count; - return this; - } - - @NonNull - public Where offset(int offset) { - this.offset = offset; - return this; - } - - /** - * Specify that we use an EXISTS statement for this Where class. - * - * @param where The query to use in the EXISTS clause. Such as SELECT * FROM `MyTable` WHERE ... etc. - * @return This where with an EXISTS clause. - */ - @NonNull - public Where exists(@NonNull Where where) { - operatorGroup.and(new ExistenceOperator() - .where(where)); - return this; - } - - @NonNull - @Override - public BaseModel.Action getPrimaryAction() { - return whereBase.getPrimaryAction(); - } - - @Override - public String getQuery() { - String fromQuery = whereBase.getQuery().trim(); - QueryBuilder queryBuilder = new QueryBuilder().append(fromQuery).appendSpace() - .appendQualifier("WHERE", operatorGroup.getQuery()) - .appendQualifier("GROUP BY", QueryBuilder.join(",", groupByList)) - .appendQualifier("HAVING", havingGroup.getQuery()) - .appendQualifier("ORDER BY", QueryBuilder.join(",", orderByList)); - - if (limit > VALUE_UNSET) { - queryBuilder.appendQualifier("LIMIT", String.valueOf(limit)); - } - if (offset > VALUE_UNSET) { - queryBuilder.appendQualifier("OFFSET", String.valueOf(offset)); - } - - return queryBuilder.getQuery(); - } - - /** - * @return the result of the query as a {@link Cursor}. - */ - @Override - public FlowCursor query(@NonNull DatabaseWrapper wrapper) { - // Query the sql here - FlowCursor cursor; - if (whereBase.getQueryBuilderBase() instanceof Select) { - cursor = wrapper.rawQuery(getQuery(), null); - } else { - cursor = super.query(wrapper); - } - - return cursor; - } - - @Override - public FlowCursor query() { - return query(FlowManager.getDatabaseForTable(getTable()).getWritableDatabase()); - } - - /** - * Queries for all of the results this statement returns from a DB cursor in the form of the {@link TModel} - * - * @return All of the entries in the DB converted into {@link TModel} - */ - @NonNull - @Override - public List queryList() { - checkSelect("query"); - return super.queryList(); - } - - /** - * Queries and returns only the first {@link TModel} result from the DB. Will enforce a limit of 1 item - * returned from the database. - * - * @return The first result of this query. Note: this query forces a limit of 1 from the database. - */ - @Override - public TModel querySingle() { - checkSelect("query"); - limit(1); - return super.querySingle(); - } - - @NonNull - public WhereBase getWhereBase() { - return whereBase; - } - - private void checkSelect(String methodName) { - if (!(whereBase.getQueryBuilderBase() instanceof Select)) { - throw new IllegalArgumentException("Please use " + methodName + "(). The beginning is not a ISelect"); - } - } - - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/WhereBase.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/WhereBase.java deleted file mode 100644 index cd5ff9b3e..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/WhereBase.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.Query; - -/** - * Description: The base for a {@link Where} statement. - */ -public interface WhereBase extends Query, Actionable { - - /** - * @return The table of this query. - */ - @NonNull - Class getTable(); - - /** - * @return The base Query object. - */ - @NonNull - Query getQueryBuilderBase(); - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IProperty.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IProperty.java deleted file mode 100644 index 7ffc60677..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IProperty.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language.property; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.language.Join; -import com.raizlabs.android.dbflow.sql.language.Method; -import com.raizlabs.android.dbflow.sql.language.NameAlias; -import com.raizlabs.android.dbflow.sql.language.OrderBy; -import com.raizlabs.android.dbflow.structure.Model; - -/** - * Description: Defines the base interface all property classes implement. - */ -public interface IProperty

extends Query { - - /** - * @param aliasName The name of the alias. - * @return A new {@link P} that expresses the current column name with the specified Alias name. - */ - @NonNull - P as(@NonNull String aliasName); - - /** - * Adds another property and returns as a new property. i.e p1 + p2 - * - * @param iProperty the property to add. - * @return A new instance. - */ - @NonNull - P plus(@NonNull IProperty iProperty); - - /** - * Subtracts another property and returns as a new property. i.e p1 - p2 - * - * @param iProperty the property to subtract. - * @return A new instance. - */ - @NonNull - P minus(@NonNull IProperty iProperty); - - /** - * Divides another property and returns as a new property. i.e p1 / p2 - * - * @param iProperty the property to divide. - * @return A new instance. - */ - @NonNull - P div(@NonNull IProperty iProperty); - - /** - * Multiplies another property and returns as a new property. i.e p1 * p2 - * - * @param iProperty the property to multiply. - * @return A new instance. - */ - P times(@NonNull IProperty iProperty); - - /** - * Modulous another property and returns as a new property. i.e p1 % p2 - * - * @param iProperty the property to calculate remainder of. - * @return A new instance. - */ - @NonNull - P rem(@NonNull IProperty iProperty); - - /** - * Concats another property and returns as a new propert.y i.e. p1 || p2 - * - * @param iProperty The property to concatenate. - * @return A new instance. - */ - @NonNull - P concatenate(@NonNull IProperty iProperty); - - /** - * @return Appends DISTINCT to the property name. This is handy in {@link Method} queries. - * This distinct {@link P} can only be used with one column within a {@link Method}. - */ - @NonNull - P distinct(); - - /** - * @return A property that represents the {@link Model} from which it belongs to. This is useful - * in {@link Join} queries to represent this property. - *

- * The resulting {@link P} becomes `tableName`.`columnName`. - */ - @NonNull - P withTable(); - - /** - * @param tableNameAlias The name of the table to append. This may be different because of complex queries - * that use a {@link NameAlias} for the table name. - * @return A property that represents the {@link Model} from which it belongs to. This is useful - * in {@link Join} queries to represent this property. - *

- * The resulting column name becomes `tableName`.`columnName`. - */ - @NonNull - P withTable(@NonNull NameAlias tableNameAlias); - - /** - * @return The underlying {@link NameAlias} that represents the name of this property. - */ - @NonNull - NameAlias getNameAlias(); - - /** - * @return The key used in placing values into cursor. - */ - @NonNull - String getCursorKey(); - - /** - * @return the table this property belongs to. - */ - @NonNull - Class getTable(); - - @NonNull - OrderBy asc(); - - @NonNull - OrderBy desc(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IndexProperty.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IndexProperty.java deleted file mode 100644 index dcb2d5061..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/IndexProperty.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language.property; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.language.Index; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Defines an INDEX in Sqlite. It basically speeds up data retrieval over large datasets. - * It gets generated from {@link Table#indexGroups()}, but also can be manually constructed. These are activated - * and deactivated manually. - */ -public class IndexProperty { - - private final Index index; - - public IndexProperty(String indexName, boolean unique, Class table, IProperty... properties) { - index = SQLite.index(indexName); - index.on(table, properties) - .unique(unique); - } - - public void createIfNotExists(@NonNull DatabaseWrapper wrapper) { - index.enable(wrapper); - } - - public void createIfNotExists() { - index.enable(); - } - - public void drop() { - index.disable(); - } - - public void drop(DatabaseWrapper writableDatabase) { - index.disable(writableDatabase); - } - - public Index getIndex() { - return index; - } - - public String getIndexName() { - return QueryBuilder.quoteIfNeeded(index.getIndexName()); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/Property.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/Property.java deleted file mode 100644 index 863308993..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/Property.java +++ /dev/null @@ -1,539 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language.property; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.language.BaseModelQueriable; -import com.raizlabs.android.dbflow.sql.language.IConditional; -import com.raizlabs.android.dbflow.sql.language.IOperator; -import com.raizlabs.android.dbflow.sql.language.NameAlias; -import com.raizlabs.android.dbflow.sql.language.Operator; -import com.raizlabs.android.dbflow.sql.language.OrderBy; - -import java.util.Collection; - -import static com.raizlabs.android.dbflow.sql.language.Operator.op; - -/** - * Description: The main, immutable property class that gets generated from a table definition. - *

- * This class delegates all of its {@link IOperator} methods to a new {@link Operator} that's used - * in the SQLite query language. - *

- * This ensures that the language is strictly type-safe and only declared - * columns get used. Also any calls on the methods return a new {@link Property}. - *

- * This is type parametrized so that all values passed to this class remain properly typed. - */ -public class Property implements IProperty>, IConditional, IOperator { - - public static final Property ALL_PROPERTY = new Property<>(null, NameAlias.rawBuilder("*").build()); - - public static final Property WILDCARD = new Property(null, NameAlias.rawBuilder("?").build()); - - public static Property allProperty(Class table) { - return new Property(table, NameAlias.rawBuilder("*").build()).withTable(); - } - - @Nullable final Class table; - - protected NameAlias nameAlias; - - public Property(@Nullable Class table, @NonNull NameAlias nameAlias) { - this.table = table; - this.nameAlias = nameAlias; - } - - public Property(@Nullable Class table, @Nullable String columnName) { - this.table = table; - if (columnName != null) { - nameAlias = new NameAlias.Builder(columnName).build(); - } - } - - public Property(@Nullable Class table, @NonNull String columnName, @NonNull String aliasName) { - this(table, NameAlias.builder(columnName).as(aliasName).build()); - } - - @NonNull - @Override - public Property withTable() { - return withTable(new NameAlias.Builder(FlowManager.getTableName(table)).build()); - } - - @NonNull - @Override - public NameAlias getNameAlias() { - return nameAlias; - } - - @Override - public String getQuery() { - return getNameAlias().getQuery(); - } - - @NonNull - @Override - public String getCursorKey() { - return getNameAlias().getQuery(); - } - - @NonNull - public String getDefinition() { - return getNameAlias().getFullQuery(); - } - - @Override - public String toString() { - return getNameAlias().toString(); - } - - @NonNull - @Override - public Operator is(@NonNull IConditional conditional) { - return getCondition().is(conditional); - } - - @NonNull - @Override - public Operator eq(@NonNull IConditional conditional) { - return getCondition().eq(conditional); - } - - @NonNull - @Override - public Operator isNot(@NonNull IConditional conditional) { - return getCondition().isNot(conditional); - } - - @NonNull - @Override - public Operator notEq(@NonNull IConditional conditional) { - return getCondition().notEq(conditional); - } - - @NonNull - @Override - public Operator like(@NonNull IConditional conditional) { - return getCondition().like(conditional); - } - - @NonNull - @Override - public Operator glob(@NonNull IConditional conditional) { - return getCondition().glob(conditional); - } - - @NonNull - @Override - public Operator like(@NonNull String value) { - return getCondition().like(value); - } - - @NonNull - @Override - public Operator notLike(@NonNull String value) { - return getCondition().notLike(value); - } - - @NonNull - @Override - public Operator glob(@NonNull String value) { - return getCondition().glob(value); - } - - @NonNull - @Override - public Operator greaterThan(@NonNull IConditional conditional) { - return getCondition().greaterThan(conditional); - } - - @NonNull - @Override - public Operator greaterThanOrEq(@NonNull IConditional conditional) { - return getCondition().greaterThanOrEq(conditional); - } - - @NonNull - @Override - public Operator lessThan(@NonNull IConditional conditional) { - return getCondition().lessThan(conditional); - } - - @NonNull - @Override - public Operator lessThanOrEq(@NonNull IConditional conditional) { - return getCondition().lessThanOrEq(conditional); - } - - @NonNull - @Override - public Operator.Between between(@NonNull IConditional conditional) { - return getCondition().between(conditional); - } - - @NonNull - @Override - public Operator.In in(@NonNull IConditional firstConditional, @NonNull IConditional... conditionals) { - return getCondition().in(firstConditional, conditionals); - } - - @NonNull - @Override - public Operator.In notIn(@NonNull IConditional firstConditional, @NonNull IConditional... conditionals) { - return getCondition().notIn(firstConditional, conditionals); - } - - @NonNull - @Override - public Operator is(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().is(baseModelQueriable); - } - - @NonNull - @Override - public Operator isNull() { - return getCondition().isNull(); - } - - @NonNull - @Override - public Operator eq(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().eq(baseModelQueriable); - } - - @NonNull - @Override - public Operator isNot(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().isNot(baseModelQueriable); - } - - @NonNull - @Override - public Operator isNotNull() { - return getCondition().isNotNull(); - } - - @NonNull - @Override - public Operator notEq(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().notEq(baseModelQueriable); - } - - @NonNull - @Override - public Operator like(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().like(baseModelQueriable); - } - - @NonNull - @Override - public Operator notLike(@NonNull IConditional conditional) { - return getCondition().notLike(conditional); - } - - @NonNull - @Override - public Operator notLike(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().notLike(baseModelQueriable); - } - - @NonNull - @Override - public Operator glob(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().glob(baseModelQueriable); - } - - @NonNull - @Override - public Operator greaterThan(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().greaterThan(baseModelQueriable); - } - - @NonNull - @Override - public Operator greaterThanOrEq(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().greaterThanOrEq(baseModelQueriable); - } - - @NonNull - @Override - public Operator lessThan(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().lessThan(baseModelQueriable); - } - - @NonNull - @Override - public Operator lessThanOrEq(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().lessThanOrEq(baseModelQueriable); - } - - @NonNull - @Override - public Operator.Between between(@NonNull BaseModelQueriable baseModelQueriable) { - return getCondition().between(baseModelQueriable); - } - - @NonNull - @Override - public Operator.In in(@NonNull BaseModelQueriable firstBaseModelQueriable, @NonNull BaseModelQueriable... baseModelQueriables) { - return getCondition().in(firstBaseModelQueriable, baseModelQueriables); - } - - @NonNull - @Override - public Operator.In notIn(@NonNull BaseModelQueriable firstBaseModelQueriable, @NonNull BaseModelQueriable... baseModelQueriables) { - return getCondition().notIn(firstBaseModelQueriable, baseModelQueriables); - } - - @NonNull - @Override - public Operator concatenate(@NonNull IConditional conditional) { - return getCondition().concatenate(conditional); - } - - @NonNull - @Override - public Operator plus(@NonNull BaseModelQueriable value) { - return getCondition().plus(value); - } - - @NonNull - @Override - public Operator minus(@NonNull BaseModelQueriable value) { - return getCondition().minus(value); - } - - @NonNull - @Override - public Operator div(@NonNull BaseModelQueriable value) { - return getCondition().div(value); - } - - @NonNull - @Override - public Operator times(@NonNull BaseModelQueriable value) { - return getCondition().times(value); - } - - @NonNull - @Override - public Operator rem(@NonNull BaseModelQueriable value) { - return getCondition().rem(value); - } - - @NonNull - @Override - public Class getTable() { - return table; - } - - @NonNull - @Override - public Property plus(@NonNull IProperty iProperty) { - return new Property<>(table, NameAlias.joinNames(Operator.Operation.PLUS, - nameAlias.fullName(), iProperty.toString())); - } - - @NonNull - @Override - public Property minus(@NonNull IProperty iProperty) { - return new Property<>(table, NameAlias.joinNames(Operator.Operation.MINUS, - nameAlias.fullName(), iProperty.toString())); - } - - @NonNull - @Override - public Property div(@NonNull IProperty iProperty) { - return new Property<>(table, NameAlias.joinNames(Operator.Operation.DIVISION, - nameAlias.fullName(), iProperty.toString())); - } - - @Override - public Property times(@NonNull IProperty iProperty) { - return new Property<>(table, NameAlias.joinNames(Operator.Operation.MULTIPLY, - nameAlias.fullName(), iProperty.toString())); - } - - @NonNull - @Override - public Property rem(@NonNull IProperty iProperty) { - return new Property<>(table, NameAlias.joinNames(Operator.Operation.MOD, - nameAlias.fullName(), iProperty.toString())); - } - - @NonNull - @Override - public Property concatenate(@NonNull IProperty iProperty) { - return new Property<>(table, NameAlias.joinNames(Operator.Operation.CONCATENATE, - nameAlias.fullName(), iProperty.toString())); - } - - @NonNull - @Override - public Property as(@NonNull String aliasName) { - return new Property<>(table, getNameAlias() - .newBuilder() - .as(aliasName) - .build()); - } - - @NonNull - @Override - public Property distinct() { - return new Property<>(table, getDistinctAliasName()); - } - - @NonNull - @Override - public Property withTable(@NonNull NameAlias tableNameAlias) { - return new Property<>(table, getNameAlias() - .newBuilder() - .withTable(tableNameAlias.getQuery()) - .build()); - } - - @NonNull - @Override - public Operator is(@Nullable T value) { - return getCondition().is(value); - } - - @NonNull - @Override - public Operator eq(@Nullable T value) { - return getCondition().eq(value); - } - - @NonNull - @Override - public Operator isNot(@Nullable T value) { - return getCondition().isNot(value); - } - - @NonNull - @Override - public Operator notEq(@Nullable T value) { - return getCondition().notEq(value); - } - - @NonNull - @Override - public Operator greaterThan(@NonNull T value) { - return getCondition().greaterThan(value); - } - - @NonNull - @Override - public Operator greaterThanOrEq(@NonNull T value) { - return getCondition().greaterThanOrEq(value); - } - - @NonNull - @Override - public Operator lessThan(@NonNull T value) { - return getCondition().lessThan(value); - } - - @NonNull - @Override - public Operator lessThanOrEq(@NonNull T value) { - return getCondition().lessThanOrEq(value); - } - - @NonNull - @Override - public Operator.Between between(@NonNull T value) { - return getCondition().between(value); - } - - @NonNull - @SuppressWarnings({"ConfusingArgumentToVarargsMethod", "unchecked"}) - @Override - public Operator.In in(@NonNull T firstValue, T... values) { - return getCondition().in(firstValue, values); - } - - @NonNull - @SuppressWarnings({"ConfusingArgumentToVarargsMethod", "unchecked"}) - @Override - public Operator.In notIn(@NonNull T firstValue, T... values) { - return getCondition().notIn(firstValue, values); - } - - @NonNull - @Override - public Operator.In in(@NonNull Collection values) { - return getCondition().in(values); - } - - @NonNull - @Override - public Operator.In notIn(@NonNull Collection values) { - return getCondition().notIn(values); - } - - @NonNull - @Override - public Operator concatenate(@Nullable T value) { - return getCondition().concatenate(value); - } - - @NonNull - @Override - public Operator plus(@NonNull T value) { - return getCondition().plus(value); - } - - @NonNull - @Override - public Operator minus(@NonNull T value) { - return getCondition().minus(value); - } - - @NonNull - @Override - public Operator div(@NonNull T value) { - return getCondition().div(value); - } - - @Override - public Operator times(@NonNull T value) { - return getCondition().times(value); - } - - @NonNull - @Override - public Operator rem(@NonNull T value) { - return getCondition().rem(value); - } - - - @Override - @NonNull - public OrderBy asc() { - return OrderBy.fromProperty(this).ascending(); - } - - @Override - @NonNull - public OrderBy desc() { - return OrderBy.fromProperty(this).descending(); - } - - /** - * @return helper method to construct it in a {@link #distinct()} call. - */ - protected NameAlias getDistinctAliasName() { - return getNameAlias() - .newBuilder() - .distinct() - .build(); - } - - @NonNull - protected Operator getCondition() { - return op(getNameAlias()); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/PropertyFactory.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/PropertyFactory.java deleted file mode 100644 index 6b6457b5d..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/PropertyFactory.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language.property; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.language.NameAlias; -import com.raizlabs.android.dbflow.sql.language.Operator; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; - -/** - * Description: Provides some useful methods for creating {@link IProperty} from non-property types. - */ -public class PropertyFactory { - - /** - * Converts a char into a {@link Property} as its value represented by string. - * - * @param c the char to convert. - * @return A new property. - */ - @NonNull - public static Property from(char c) { - return new Property<>(null, NameAlias.rawBuilder("'" + c + "'") - .build()); - } - - /** - * Converts a int into a {@link Property} as its value represented by string. - * - * @param i the int to convert. - * @return A new property. - */ - @NonNull - public static Property from(int i) { - return new Property<>(null, NameAlias.rawBuilder(i + "") - .build()); - } - - /** - * Converts a double into a {@link Property} as its value represented by string. - * - * @param d the double to convert. - * @return A new property. - */ - @NonNull - public static Property from(double d) { - return new Property<>(null, NameAlias.rawBuilder(d + "") - .build()); - } - - /** - * Converts a long into a {@link Property} as its value represented by string. - * - * @param l the long to convert. - * @return A new property. - */ - @NonNull - public static Property from(long l) { - return new Property<>(null, NameAlias.rawBuilder(l + "") - .build()); - } - - /** - * Converts a float into a {@link Property} as its value represented by string. - * - * @param f the float to convert. - * @return A new property. - */ - @NonNull - public static Property from(float f) { - return new Property<>(null, NameAlias.rawBuilder(f + "") - .build()); - } - - /** - * Converts a short into a {@link Property} as its value represented by string. - * - * @param s the short to convert. - * @return A new property. - */ - @NonNull - public static Property from(short s) { - return new Property<>(null, NameAlias.rawBuilder(s + "") - .build()); - } - - /** - * Converts a byte into a {@link Property} as its value represented by string. - * - * @param b the byte to convert. - * @return A new property. - */ - @NonNull - public static Property from(byte b) { - return new Property<>(null, NameAlias.rawBuilder(b + "") - .build()); - } - - /** - * Creates a new type-parameterized {@link Property} to be used as its value represented by a string - * using {@link Operator#convertValueToString(Object)}. - *

- * It will not convert a String column name - * into a property, rather it assumes its database value represented by the String. - * - * @param type The object with value to use. - * @param The parameter of its type. - * @return A new property with its type. - */ - @NonNull - public static Property from(@Nullable T type) { - return new Property<>(null, NameAlias.rawBuilder( - Operator.convertValueToString(type)) - .build()); - } - - /** - * Creates a new {@link Property} that is used to allow selects in a query. - * - * @param queriable The queriable to use and evaulated into a query. - * @param The model class of the query. - * @return A new property that is a query. - */ - @NonNull - public static Property from(@NonNull ModelQueriable queriable) { - return from(queriable.getTable(), "(" + String.valueOf(queriable.getQuery()).trim() + ")"); - } - - /** - * Creates a new type-parameterized {@link Property} to be used as its value represented by the string passed in. - * - * @param type The type to return. - * @param stringRepresentation The string representation of the object you wish to use. - * @param The parameter of its type. - * @return A new property with its type. - */ - @NonNull - public static Property from(@Nullable Class type, @Nullable String stringRepresentation) { - return new Property<>(null, NameAlias.rawBuilder(stringRepresentation) - .build()); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/TypeConvertedProperty.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/TypeConvertedProperty.java deleted file mode 100644 index 0fa7eca83..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/TypeConvertedProperty.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language.property; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.converter.TypeConverter; -import com.raizlabs.android.dbflow.sql.language.NameAlias; -import com.raizlabs.android.dbflow.sql.language.Operator; -import com.raizlabs.android.dbflow.structure.ModelAdapter; - -import static com.raizlabs.android.dbflow.sql.language.Operator.op; - -/** - * Description: Provides convenience methods for {@link TypeConverter} when constructing queries. - * - * @author Andrew Grosner (fuzz) - */ - -public class TypeConvertedProperty extends Property { - - /** - * Generated by the compiler, looks up the type converter based on {@link ModelAdapter} when needed. - * This is so we can properly retrieve the type converter at any time. - */ - public interface TypeConverterGetter { - - TypeConverter getTypeConverter(Class modelClass); - } - - private TypeConvertedProperty databaseProperty; - - private boolean convertToDB; - - private final TypeConverterGetter getter; - - public TypeConvertedProperty(Class table, NameAlias nameAlias, - boolean convertToDB, - TypeConverterGetter getter) { - super(table, nameAlias); - this.convertToDB = convertToDB; - this.getter = getter; - } - - public TypeConvertedProperty(Class table, String columnName, - boolean convertToDB, - TypeConverterGetter getter) { - super(table, columnName); - this.convertToDB = convertToDB; - this.getter = getter; - } - - @NonNull - @Override - protected Operator getCondition() { - return op(getNameAlias(), getter.getTypeConverter(table), convertToDB); - } - - /** - * @return A new {@link Property} that corresponds to the inverted type of the {@link TypeConvertedProperty}. - * Provides a convenience for supplying type converted methods within the DataClass of the {@link TypeConverter} - */ - @NonNull - public Property invertProperty() { - if (databaseProperty == null) { - databaseProperty = new TypeConvertedProperty<>(table, nameAlias, - !convertToDB, new TypeConverterGetter() { - @Override - public TypeConverter getTypeConverter(Class modelClass) { - return getter.getTypeConverter(modelClass); - } - }); - } - return databaseProperty; - } - - @NonNull - @Override - public Property withTable(@NonNull NameAlias tableNameAlias) { - NameAlias nameAlias = this.getNameAlias() - .newBuilder() - .withTable(tableNameAlias.getQuery()) - .build(); - return new TypeConvertedProperty<>(this.getTable(), nameAlias, this.convertToDB, this.getter); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/WrapperProperty.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/WrapperProperty.java deleted file mode 100644 index e7e3dba35..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/property/WrapperProperty.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.raizlabs.android.dbflow.sql.language.property; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.language.NameAlias; - -/** - * Description: Provides convenience for types that are represented in different ways in the DB. - * - * @author Andrew Grosner (fuzz) - */ -public class WrapperProperty extends Property { - - private WrapperProperty databaseProperty; - - public WrapperProperty(@NonNull Class table, @NonNull NameAlias nameAlias) { - super(table, nameAlias); - } - - public WrapperProperty(@NonNull Class table, @NonNull String columnName) { - super(table, columnName); - } - - /** - * @return A new {@link Property} that corresponds to the inverted type of the {@link WrapperProperty}. Convenience - * for types that have different DB representations. - */ - @SuppressWarnings("ConstantConditions") - @NonNull - public Property invertProperty() { - if (databaseProperty == null) { - databaseProperty = new WrapperProperty<>(table, nameAlias); - } - return databaseProperty; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/AlterTableMigration.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/AlterTableMigration.java deleted file mode 100644 index 75d17f6de..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/AlterTableMigration.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.raizlabs.android.dbflow.sql.migration; - -import android.database.Cursor; -import android.support.annotation.CallSuper; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.SQLiteType; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: Provides a very nice way to alter a single table quickly and easily. - */ -public class AlterTableMigration extends BaseMigration { - - /** - * The table to ALTER - */ - private final Class table; - - /** - * The query we use - */ - private QueryBuilder query; - - /** - * The query to rename the table with - */ - private QueryBuilder renameQuery; - - /** - * The columns to ALTER within a table - */ - private List columnDefinitions; - - private List columnNames; - - /** - * The old name of the table before renaming it - */ - private String oldTableName; - - public AlterTableMigration(Class table) { - this.table = table; - } - - @Override - public final void migrate(@NonNull DatabaseWrapper database) { - // "ALTER TABLE " - String sql = getAlterTableQueryBuilder().getQuery(); - String tableName = FlowManager.getTableName(table); - - // "{oldName} RENAME TO {newName}" - // Since the structure has been updated already, the manager knows only the new name. - if (renameQuery != null) { - String renameQuery = new QueryBuilder(sql).appendQuotedIfNeeded(oldTableName) - .append(this.renameQuery.getQuery()) - .append(tableName) - .toString(); - database.execSQL(renameQuery); - } - - // We have column definitions to add here - // ADD COLUMN columnName {type} - if (columnDefinitions != null) { - - Cursor cursorToCheckColumnFor = SQLite.select().from(table).limit(0).query(database); - if (cursorToCheckColumnFor != null) { - try { - sql = new QueryBuilder(sql).append(tableName).toString(); - for (int i = 0; i < columnDefinitions.size(); i++) { - QueryBuilder columnDefinition = columnDefinitions.get(i); - String columnName = QueryBuilder.stripQuotes(columnNames.get(i)); - if (cursorToCheckColumnFor.getColumnIndex(columnName) == -1) { - database.execSQL(sql + " ADD COLUMN " + columnDefinition.getQuery()); - } - } - } finally { - cursorToCheckColumnFor.close(); - } - } - } - } - - @CallSuper - @Override - public void onPostMigrate() { - // cleanup and make fields eligible for garbage collection - query = null; - renameQuery = null; - columnDefinitions = null; - columnNames = null; - } - - /** - * Call this to rename a table to a new name, such as changing either the {@link com.raizlabs.android.dbflow.structure.Model} class name - * or by changing the name through a {@link com.raizlabs.android.dbflow.annotation.Table} - * - * @param oldName The new name to call the table. - * @return This instance - */ - public AlterTableMigration renameFrom(@NonNull String oldName) { - oldTableName = oldName; - renameQuery = new QueryBuilder().append(" RENAME").appendSpaceSeparated("TO"); - return this; - } - - /** - * Add a column to the DB. This does not necessarily need to be reflected in the {@link TModel}, - * but it is recommended. - * - * @param sqLiteType The type of column represented in the DB. - * @param columnName The name of the column to add. Use the "_Table" class for the specified table. - * @return This instance - */ - public AlterTableMigration addColumn(@NonNull SQLiteType sqLiteType, @NonNull String columnName) { - if (columnDefinitions == null) { - columnDefinitions = new ArrayList<>(); - columnNames = new ArrayList<>(); - } - - QueryBuilder queryBuilder = new QueryBuilder() - .append(QueryBuilder.quoteIfNeeded(columnName)).appendSpace().appendSQLiteType(sqLiteType); - columnDefinitions.add(queryBuilder); - columnNames.add(columnName); - - return this; - } - - /** - * Add a column to the DB. This does not necessarily need to be reflected in the {@link TModel}, - * but it is recommended. - * - * @param sqLiteType The type of column that pertains to an {@link SQLiteType} - * @param columnName The name of the column to add. Use the "$Table" class for the specified table. - * @param referenceClause The clause of the references that this foreign key points to. - * @return This instance - */ - public AlterTableMigration addForeignKeyColumn(SQLiteType sqLiteType, String columnName, String referenceClause) { - if (columnDefinitions == null) { - columnDefinitions = new ArrayList<>(); - columnNames = new ArrayList<>(); - } - - QueryBuilder queryBuilder = new QueryBuilder() - .append(QueryBuilder.quoteIfNeeded(columnName)).appendSpace().appendSQLiteType(sqLiteType) - .appendSpace().append("REFERENCES ").append(referenceClause); - columnDefinitions.add(queryBuilder); - columnNames.add(columnName); - - return this; - } - - /** - * @return The query that renames the table. - */ - public String getRenameQuery() { - QueryBuilder queryBuilder = new QueryBuilder(getAlterTableQueryBuilder().getQuery()).appendQuotedIfNeeded(oldTableName) - .append(renameQuery).append(FlowManager.getTableName(table)); - return queryBuilder.getQuery(); - } - - /** - * @return A List of column definitions that add op to a table in the DB. - */ - public List getColumnDefinitions() { - String sql = new QueryBuilder(getAlterTableQueryBuilder()).append(FlowManager.getTableName(table)).toString(); - List columnDefinitions = new ArrayList<>(); - - if (this.columnDefinitions != null) { - for (QueryBuilder columnDefinition : this.columnDefinitions) { - QueryBuilder queryBuilder = new QueryBuilder(sql).appendSpaceSeparated("ADD COLUMN").append( - columnDefinition.getQuery()); - columnDefinitions.add(queryBuilder.getQuery()); - } - } - - return columnDefinitions; - } - - public QueryBuilder getAlterTableQueryBuilder() { - if (query == null) { - query = new QueryBuilder().append("ALTER").appendSpaceSeparated("TABLE"); - } - return query; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/BaseMigration.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/BaseMigration.java deleted file mode 100644 index d2a6ff687..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/BaseMigration.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.raizlabs.android.dbflow.sql.migration; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Provides the base implementation of {@link com.raizlabs.android.dbflow.sql.migration.Migration} with - * only {@link Migration#migrate(DatabaseWrapper)} needing to be implemented. - */ -public abstract class BaseMigration implements Migration { - - - @Override - public void onPreMigrate() { - - } - - @Override - public abstract void migrate(@NonNull DatabaseWrapper database); - - @Override - public void onPostMigrate() { - - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/IndexMigration.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/IndexMigration.java deleted file mode 100644 index 668a05152..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/IndexMigration.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.raizlabs.android.dbflow.sql.migration; - -import android.support.annotation.CallSuper; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.language.Index; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Defines and enables an Index structurally through a migration. - */ -public abstract class IndexMigration extends BaseMigration { - - /** - * The table to index on - */ - private Class onTable; - - /** - * The underlying index object. - */ - private Index index; - - public IndexMigration(@NonNull Class onTable) { - this.onTable = onTable; - } - - @NonNull - public abstract String getName(); - - @CallSuper - @Override - public void onPreMigrate() { - index = getIndex(); - } - - @Override - public final void migrate(@NonNull DatabaseWrapper database) { - database.execSQL(getIndex().getQuery()); - } - - @CallSuper - @Override - public void onPostMigrate() { - onTable = null; - index = null; - } - - /** - * Adds a column to the underlying INDEX - * - * @param property The name of the column to add to the Index - * @return This migration - */ - @NonNull - public IndexMigration addColumn(IProperty property) { - getIndex().and(property); - return this; - } - - /** - * Sets the INDEX to UNIQUE - * - * @return This migration. - */ - @NonNull - public IndexMigration unique() { - getIndex().unique(true); - return this; - } - - /** - * @return The index object based on the contents of this migration. - */ - @NonNull - public Index getIndex() { - if (index == null) { - index = new Index(getName()).on(onTable); - } - return index; - } - - /** - * @return the query backing this migration. - */ - @NonNull - public String getIndexQuery() { - return getIndex().getQuery(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/IndexPropertyMigration.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/IndexPropertyMigration.java deleted file mode 100644 index 6a7a7ab68..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/IndexPropertyMigration.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.raizlabs.android.dbflow.sql.migration; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.language.property.IndexProperty; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Allows you to specify if and when an {@link IndexProperty} gets used or created. - */ -public abstract class IndexPropertyMigration extends BaseMigration { - - @NonNull - public abstract IndexProperty getIndexProperty(); - - /** - * @return true if create the index, false to drop the index. - */ - public boolean shouldCreate() { - return true; - } - - @Override - public void migrate(@NonNull DatabaseWrapper database) { - if (shouldCreate()) { - getIndexProperty().createIfNotExists(database); - } else { - getIndexProperty().drop(database); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigration.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigration.java deleted file mode 100644 index 5d4824444..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/UpdateTableMigration.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.raizlabs.android.dbflow.sql.migration; - -import android.support.annotation.CallSuper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.language.BaseQueriable; -import com.raizlabs.android.dbflow.sql.language.OperatorGroup; -import com.raizlabs.android.dbflow.sql.language.SQLOperator; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Provides a simple way to update a table's field or fields quickly in a migration. - * It ties an SQLite {@link com.raizlabs.android.dbflow.sql.language.Update} - * to migrations whenever we want to batch update tables in a structured manner. - */ -public class UpdateTableMigration extends BaseMigration { - - /** - * The table to update - */ - private final Class table; - - /** - * Builds the conditions for the WHERE part of our query - */ - @Nullable - private OperatorGroup whereOperatorGroup; - - /** - * The conditions to use to set fields in the update query - */ - @Nullable - private OperatorGroup setOperatorGroup; - - /** - * Creates an update migration. - * - * @param table The table to update - */ - public UpdateTableMigration(@NonNull Class table) { - this.table = table; - } - - /** - * This will append a condition to this migration. It will execute each of these in succession with the order - * that this is called. - * - * @param conditions The conditions to append - */ - @NonNull - public UpdateTableMigration set(SQLOperator... conditions) { - if (setOperatorGroup == null) { - setOperatorGroup = OperatorGroup.nonGroupingClause(); - } - - setOperatorGroup.andAll(conditions); - return this; - } - - @NonNull - public UpdateTableMigration where(SQLOperator... conditions) { - if (whereOperatorGroup == null) { - whereOperatorGroup = OperatorGroup.nonGroupingClause(); - } - - whereOperatorGroup.andAll(conditions); - return this; - } - - @Override - public final void migrate(@NonNull DatabaseWrapper database) { - getUpdateStatement().execute(database); - } - - @CallSuper - @Override - public void onPostMigrate() { - // make fields eligible for GC - setOperatorGroup = null; - whereOperatorGroup = null; - } - - @NonNull - public BaseQueriable getUpdateStatement() { - return SQLite.update(table) - .set(setOperatorGroup) - .where(whereOperatorGroup); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/AsyncQuery.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/AsyncQuery.java deleted file mode 100644 index b44cd8fd6..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/AsyncQuery.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.BaseAsyncObject; -import com.raizlabs.android.dbflow.structure.database.transaction.QueryTransaction; -import com.raizlabs.android.dbflow.structure.database.transaction.QueryTransaction.QueryResultCallback; -import com.raizlabs.android.dbflow.structure.database.transaction.QueryTransaction.QueryResultListCallback; -import com.raizlabs.android.dbflow.structure.database.transaction.QueryTransaction.QueryResultSingleCallback; - -/** - * Description: Adds async methods to a {@link ModelQueriable} - */ -public class AsyncQuery extends BaseAsyncObject> { - - private final ModelQueriable modelQueriable; - private QueryResultCallback queryResultCallback; - private QueryResultListCallback queryResultListCallback; - private QueryResultSingleCallback queryResultSingleCallback; - - /** - * Constructs an instance of this async query. - * - * @param queriable The queriable object to use to query data. - */ - public AsyncQuery(@NonNull ModelQueriable queriable) { - super(queriable.getTable()); - this.modelQueriable = queriable; - } - - /** - * @param queryResultCallback Called when query is executed and has a result. - */ - public AsyncQuery queryResultCallback(@NonNull QueryResultCallback queryResultCallback) { - this.queryResultCallback = queryResultCallback; - return this; - } - - /** - * @param queryResultSingleCallback Called when query is executed and has a result. - */ - public AsyncQuery querySingleResultCallback(@NonNull QueryResultSingleCallback queryResultSingleCallback) { - this.queryResultSingleCallback = queryResultSingleCallback; - return this; - } - - /** - * @param queryResultListCallback Called when query is executed and has a result. - */ - public AsyncQuery queryListResultCallback(@NonNull QueryResultListCallback queryResultListCallback) { - this.queryResultListCallback = queryResultListCallback; - return this; - } - - /** - * Runs the specified query in the background. - */ - public void execute() { - executeTransaction(new QueryTransaction.Builder<>(modelQueriable) - .queryResult(queryResultCallback) - .queryListResult(queryResultListCallback) - .querySingleResult(queryResultSingleCallback) - .build()); - } - - /** - * @return The table this Query is associated with. - */ - public Class getTable() { - return modelQueriable.getTable(); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableListModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableListModelLoader.java deleted file mode 100644 index 99a7c07f6..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableListModelLoader.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.cache.ModelCache; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: Loads a {@link List} of {@link TModel} with {@link Table#cachingEnabled()} true. - */ -public class CacheableListModelLoader extends ListModelLoader { - - private ModelAdapter modelAdapter; - private ModelCache modelCache; - - public CacheableListModelLoader(@NonNull Class modelClass) { - super(modelClass); - } - - @NonNull - public ModelCache getModelCache() { - if (modelCache == null) { - modelCache = modelAdapter.getModelCache(); - if (modelCache == null) { - throw new IllegalArgumentException("ModelCache specified in convertToCacheableList() must not be null."); - } - } - return modelCache; - } - - @NonNull - @SuppressWarnings("unchecked") - public ModelAdapter getModelAdapter() { - if (modelAdapter == null) { - if (!(getInstanceAdapter() instanceof ModelAdapter)) { - throw new IllegalArgumentException("A non-Table type was used."); - } - modelAdapter = (ModelAdapter) getInstanceAdapter(); - if (!modelAdapter.cachingEnabled()) { - throw new IllegalArgumentException("You cannot call this method for a table that has no caching id. Either" + - "use one Primary Key or use the MultiCacheKeyConverter"); - } - } - return modelAdapter; - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public List convertToData(@NonNull FlowCursor cursor, @Nullable List data) { - if (data == null) { - data = new ArrayList<>(); - } - Object[] cacheValues = new Object[getModelAdapter().getCachingColumns().length]; - // Ensure that we aren't iterating over this cursor concurrently from different threads - if (cursor.moveToFirst()) { - do { - Object[] values = getModelAdapter().getCachingColumnValuesFromCursor(cacheValues, cursor); - TModel model = getModelCache().get(getModelAdapter().getCachingId(values)); - if (model != null) { - getModelAdapter().reloadRelationships(model, cursor); - data.add(model); - } else { - model = getModelAdapter().newInstance(); - getModelAdapter().loadFromCursor(cursor, model); - getModelCache().addModel(getModelAdapter().getCachingId(values), model); - data.add(model); - } - } while (cursor.moveToNext()); - } - return data; - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableModelLoader.java deleted file mode 100644 index cfe9744f8..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/CacheableModelLoader.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.cache.ModelCache; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: Loads model data that is backed by a {@link ModelCache}. Used when {@link Table#cachingEnabled()} - * is true. - */ -public class CacheableModelLoader extends SingleModelLoader { - - private ModelAdapter modelAdapter; - private ModelCache modelCache; - - public CacheableModelLoader(@NonNull Class modelClass) { - super(modelClass); - } - - @NonNull - @SuppressWarnings("unchecked") - public ModelAdapter getModelAdapter() { - if (modelAdapter == null) { - if (!(getInstanceAdapter() instanceof ModelAdapter)) { - throw new IllegalArgumentException("A non-Table type was used."); - } - modelAdapter = (ModelAdapter) getInstanceAdapter(); - if (!modelAdapter.cachingEnabled()) { - throw new IllegalArgumentException("You cannot call this method for a table that has no caching id. Either" + - "use one Primary Key or use the MultiCacheKeyConverter"); - } - } - return modelAdapter; - } - - @NonNull - public ModelCache getModelCache() { - if (modelCache == null) { - modelCache = getModelAdapter().getModelCache(); - } - return modelCache; - } - - /** - * Converts data by loading from cache based on its sequence of caching ids. Will reuse the passed - * {@link TModel} if it's not found in the cache and non-null. - * - * @return A model from cache. - */ - @Nullable - @Override - public TModel convertToData(@NonNull FlowCursor cursor, @Nullable TModel data, boolean moveToFirst) { - if (!moveToFirst || cursor.moveToFirst()) { - Object[] values = getModelAdapter().getCachingColumnValuesFromCursor( - new Object[getModelAdapter().getCachingColumns().length], cursor); - TModel model = getModelCache().get(getModelAdapter().getCachingId(values)); - if (model == null) { - if (data == null) { - model = getModelAdapter().newInstance(); - } else { - model = data; - } - getModelAdapter().loadFromCursor(cursor, model); - getModelCache().addModel(getModelAdapter().getCachingId(values), model); - } else { - getModelAdapter().reloadRelationships(model, cursor); - } - return model; - } else { - return null; - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ListModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ListModelLoader.java deleted file mode 100644 index 296bdacfc..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ListModelLoader.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: Loads a {@link List} of {@link TModel}. - */ -@SuppressWarnings("ConstantConditions") -public class ListModelLoader extends ModelLoader> { - - public ListModelLoader(@NonNull Class modelClass) { - super(modelClass); - } - - @NonNull - @Override - public List load(String query) { - return super.load(query); - } - - @NonNull - @Override - public List load(String query, @Nullable List data) { - return super.load(query, data); - } - - @NonNull - @Override - public List load(@NonNull DatabaseWrapper databaseWrapper, String query) { - return super.load(databaseWrapper, query); - } - - @NonNull - @Override - public List load(@NonNull DatabaseWrapper databaseWrapper, String query, - @Nullable List data) { - return super.load(databaseWrapper, query, data); - } - - @NonNull - @Override - public List load(@Nullable FlowCursor cursor) { - return super.load(cursor); - } - - @NonNull - @Override - public List load(@Nullable FlowCursor cursor, @Nullable List data) { - if (data == null) { - data = new ArrayList<>(); - } else { - data.clear(); - } - return super.load(cursor, data); - } - - @SuppressWarnings("unchecked") - @Override - @NonNull - public List convertToData(@NonNull FlowCursor cursor, @Nullable List data) { - if (data == null) { - data = new ArrayList<>(); - } else { - data.clear(); - } - - if (cursor.moveToFirst()) { - do { - TModel model = getInstanceAdapter().newInstance(); - getInstanceAdapter().loadFromCursor(cursor, model); - data.add(model); - } while (cursor.moveToNext()); - } - return data; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelLoader.java deleted file mode 100644 index 3d79d59d2..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelLoader.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.InstanceAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: Represents how models load from DB. It will query a {@link SQLiteDatabase} - * and query for a {@link Cursor}. Then the cursor is used to convert itself into an object. - */ -public abstract class ModelLoader { - - private final Class modelClass; - private DatabaseDefinition databaseDefinition; - private InstanceAdapter instanceAdapter; - - public ModelLoader(@NonNull Class modelClass) { - this.modelClass = modelClass; - } - - /** - * Loads the data from a query and returns it as a {@link TReturn}. - * - * @param query The query to call. - * @return The data loaded from the database. - */ - @Nullable - public TReturn load(@NonNull String query) { - return load(getDatabaseDefinition().getWritableDatabase(), query); - } - - @Nullable - public TReturn load(@NonNull String query, @Nullable TReturn data) { - return load(getDatabaseDefinition().getWritableDatabase(), query, data); - } - - /** - * Loads the data from a query and returns it as a {@link TReturn}. - * - * @param databaseWrapper A custom database wrapper object to use. - * @param query The query to call. - * @return The data loaded from the database. - */ - @Nullable - public TReturn load(@NonNull DatabaseWrapper databaseWrapper, @NonNull String query) { - return load(databaseWrapper, query, null); - } - - @Nullable - public TReturn load(@NonNull DatabaseWrapper databaseWrapper, @NonNull String query, - @Nullable TReturn data) { - final FlowCursor cursor = databaseWrapper.rawQuery(query, null); - return load(cursor, data); - } - - @Nullable - public TReturn load(@Nullable FlowCursor cursor) { - return load(cursor, null); - } - - @Nullable - public TReturn load(@Nullable FlowCursor cursor, @Nullable TReturn data) { - if (cursor != null) { - try { - data = convertToData(cursor, data); - } finally { - cursor.close(); - } - } - return data; - } - - @NonNull - public Class getModelClass() { - return modelClass; - } - - @NonNull - public InstanceAdapter getInstanceAdapter() { - if (instanceAdapter == null) { - instanceAdapter = FlowManager.getInstanceAdapter(modelClass); - } - return instanceAdapter; - } - - @NonNull - public DatabaseDefinition getDatabaseDefinition() { - if (databaseDefinition == null) { - databaseDefinition = FlowManager.getDatabaseForTable(modelClass); - } - return databaseDefinition; - } - - /** - * Specify how to convert the {@link Cursor} data into a {@link TReturn}. Can be null. - * - * @param cursor The cursor resulting from a query passed into {@link #load(String)} - * @param data The data (if not null) that we can reuse without need to create new object. - * @return A new (or reused) instance that represents the {@link Cursor}. - */ - @Nullable - public abstract TReturn convertToData(@NonNull final FlowCursor cursor, @Nullable TReturn data); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelQueriable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelQueriable.java deleted file mode 100644 index e40edf87b..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/ModelQueriable.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.list.FlowCursorList; -import com.raizlabs.android.dbflow.list.FlowQueryList; -import com.raizlabs.android.dbflow.sql.language.CursorResult; -import com.raizlabs.android.dbflow.sql.language.From; -import com.raizlabs.android.dbflow.sql.language.Where; -import com.raizlabs.android.dbflow.structure.BaseQueryModel; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.List; - -/** - * Description: An interface for query objects to enable you to query from the database in a structured way. - * Examples of such statements are: {@link From}, {@link Where}, {@link StringQuery} - */ -public interface ModelQueriable extends Queriable { - - /** - * @return A wrapper class around {@link Cursor} that allows you to convert its data into results. - */ - @NonNull - CursorResult queryResults(); - - /** - * @return a list of model converted items - */ - @NonNull - List queryList(); - - /** - * Allows you to specify a DB, useful for migrations. - * - * @return a list of model converted items - */ - @NonNull - List queryList(@NonNull DatabaseWrapper wrapper); - - /** - * @return Single model, the first of potentially many results - */ - @Nullable - TModel querySingle(); - - /** - * Allows you to specify a DB, useful for migrations. - * - * @return Single model, the first of potentially many results - */ - @Nullable - TModel querySingle(@NonNull DatabaseWrapper wrapper); - - /** - * @return the table that this query comes from. - */ - @NonNull - Class getTable(); - - /** - * @return A cursor-backed list that handles conversion, retrieval, and caching of lists. Can - * cache models dynamically by setting {@link FlowCursorList#setCacheModels(boolean)} to true. - */ - @NonNull - FlowCursorList cursorList(); - - /** - * @return A cursor-backed {@link List} that handles conversion, retrieval, caching, content changes, - * and more. - */ - @NonNull - FlowQueryList flowQueryList(); - - /** - * @return an async version of this query to run. - */ - @NonNull - AsyncQuery async(); - - /** - * Returns a {@link List} based on the custom {@link TQueryModel} you pass in. - * - * @param queryModelClass The query model class to use. - * @param The class that extends {@link BaseQueryModel} - * @return A list of custom models that are not tied to a table. - */ - @NonNull - List queryCustomList(@NonNull Class queryModelClass); - - /** - * Returns a single {@link TQueryModel} from this query. - * - * @param queryModelClass The class to use. - * @param The class that extends {@link BaseQueryModel} - * @return A single model from the query. - */ - @Nullable - TQueryModel queryCustomSingle(@NonNull Class queryModelClass); - - /** - * Disables caching on this query for the object retrieved from DB (if caching enabled). If - * caching is not enabled, this method is ignored. This also disables caching in a {@link FlowCursorList} - * or {@link FlowQueryList} if you {@link #flowQueryList()} or {@link #cursorList()} - */ - @NonNull - ModelQueriable disableCaching(); -} \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/Queriable.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/Queriable.java deleted file mode 100644 index e6d70f665..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/Queriable.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.language.Delete; -import com.raizlabs.android.dbflow.sql.language.Insert; -import com.raizlabs.android.dbflow.sql.language.Set; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: The most basic interface that some of the classes such as {@link Insert}, {@link ModelQueriable}, - * {@link Set}, and more implement for convenience. - */ -public interface Queriable extends Query { - - /** - * @return A cursor from the DB based on this query - */ - @Nullable - FlowCursor query(); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @param databaseWrapper The wrapper to pass in. - * @return A cursor from the DB based on this query - */ - @Nullable - FlowCursor query(@NonNull DatabaseWrapper databaseWrapper); - - - /** - * @return A new {@link DatabaseStatement} from this query. - */ - @NonNull - DatabaseStatement compileStatement(); - - /** - * @param databaseWrapper The wrapper to use. - * @return A new {@link DatabaseStatement} from this query with database specified. - */ - @NonNull - DatabaseStatement compileStatement(@NonNull DatabaseWrapper databaseWrapper); - - /** - * @return the count of the results of the query. - * @deprecated use {@link #longValue()} - */ - @Deprecated - long count(); - - /** - * @return the long value of the results of query. - */ - long longValue(); - - /** - * @return the long value of the results of query. - */ - long longValue(DatabaseWrapper databaseWrapper); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @return the count of the results of the query. - * @deprecated use {@link #longValue(DatabaseWrapper)} - */ - @Deprecated - long count(@NonNull DatabaseWrapper databaseWrapper); - - /** - * @return This may return the number of rows affected from a {@link Set} or {@link Delete} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - long executeUpdateDelete(@NonNull DatabaseWrapper databaseWrapper); - - /** - * @return This may return the number of rows affected from a {@link Set} or {@link Delete} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - long executeUpdateDelete(); - - /** - * @return This may return the number of rows affected from a {@link Insert} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - long executeInsert(); - - /** - * @return This may return the number of rows affected from a {@link Insert} statement. - * If not, returns {@link Model#INVALID_ROW_ID} - */ - long executeInsert(@NonNull DatabaseWrapper databaseWrapper); - - /** - * @return True if this query has data. It will run a {@link #count()} greater than 0. - */ - boolean hasData(); - - /** - * Allows you to pass in a {@link DatabaseWrapper} manually. - * - * @return True if this query has data. It will run a {@link #count()} greater than 0. - */ - boolean hasData(@NonNull DatabaseWrapper databaseWrapper); - - /** - * Will not return a result, rather simply will execute a SQL statement. Use this for non-SELECT statements or when - * you're not interested in the result. - */ - void execute(); - - /** - * Will not return a result, rather simply will execute a SQL statement. Use this for non-SELECT statements or when - * you're not interested in the result. - */ - void execute(@NonNull DatabaseWrapper databaseWrapper); - - @NonNull - BaseModel.Action getPrimaryAction(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableListModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableListModelLoader.java deleted file mode 100644 index d6614275e..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableListModelLoader.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: - */ -public class SingleKeyCacheableListModelLoader extends CacheableListModelLoader { - - public SingleKeyCacheableListModelLoader(@NonNull Class tModelClass) { - super(tModelClass); - } - - @NonNull - @SuppressWarnings("unchecked") - @Override - public List convertToData(@NonNull FlowCursor cursor, @Nullable List data) { - if (data == null) { - data = new ArrayList<>(); - } - Object cacheValue; - // Ensure that we aren't iterating over this cursor concurrently from different threads - if (cursor.moveToFirst()) { - do { - cacheValue = getModelAdapter().getCachingColumnValueFromCursor(cursor); - TModel model = getModelCache().get(cacheValue); - if (model != null) { - getModelAdapter().reloadRelationships(model, cursor); - data.add(model); - } else { - model = getModelAdapter().newInstance(); - getModelAdapter().loadFromCursor(cursor, model); - getModelCache().addModel(cacheValue, model); - data.add(model); - } - } while (cursor.moveToNext()); - } - return data; - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableModelLoader.java deleted file mode 100644 index 4bb81e459..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleKeyCacheableModelLoader.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: More optimized version of {@link CacheableModelLoader} which assumes that the {@link Model} - * only utilizes a single primary key. - */ -public class SingleKeyCacheableModelLoader extends CacheableModelLoader { - - public SingleKeyCacheableModelLoader(@NonNull Class modelClass) { - super(modelClass); - } - - /** - * Converts data by loading from cache based on its sequence of caching ids. Will reuse the passed - * {@link TModel} if it's not found in the cache and non-null. - * - * @return A model from cache. - */ - @Nullable - @Override - public TModel convertToData(@NonNull FlowCursor cursor, @Nullable TModel data, - boolean moveToFirst) { - if (!moveToFirst || cursor.moveToFirst()) { - Object value = getModelAdapter().getCachingColumnValueFromCursor(cursor); - TModel model = getModelCache().get(value); - if (model == null) { - if (data == null) { - model = getModelAdapter().newInstance(); - } else { - model = data; - } - getModelAdapter().loadFromCursor(cursor, model); - getModelCache().addModel(value, model); - } else { - getModelAdapter().reloadRelationships(model, cursor); - } - return model; - } else { - return null; - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleModelLoader.java deleted file mode 100644 index e1e4cabe4..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/SingleModelLoader.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: Responsible for loading data into a single object. - */ -public class SingleModelLoader extends ModelLoader { - - public SingleModelLoader(Class modelClass) { - super(modelClass); - } - - @SuppressWarnings("unchecked") - @Nullable - public TModel convertToData(@NonNull final FlowCursor cursor, @Nullable TModel data, - boolean moveToFirst) { - if (!moveToFirst || cursor.moveToFirst()) { - if (data == null) { - data = getInstanceAdapter().newInstance(); - } - getInstanceAdapter().loadFromCursor(cursor, data); - } - return data; - } - - @Override - public TModel convertToData(@NonNull final FlowCursor cursor, @Nullable TModel data) { - return convertToData(cursor, data, true); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/StringQuery.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/StringQuery.java deleted file mode 100644 index 230dd0c44..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/queriable/StringQuery.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.raizlabs.android.dbflow.sql.queriable; - -import android.database.sqlite.SQLiteDatabase; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.Query; -import com.raizlabs.android.dbflow.sql.language.BaseModelQueriable; -import com.raizlabs.android.dbflow.sql.language.Delete; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: Provides a very basic query mechanism for strings. Allows you to easily perform custom SQL query string - * code where this library does not provide. It only runs a - * {@link SQLiteDatabase#rawQuery(String, String[])}. - */ -public class StringQuery extends BaseModelQueriable implements Query, ModelQueriable { - - /** - * The full SQLite query to use - */ - private final String query; - private String[] args; - - /** - * Creates an instance of this class - * - * @param table The table to use - * @param sql The sql statement to query the DB with. Does not work with {@link Delete}, - * this must be done with {@link SQLiteDatabase#execSQL(String)} - */ - public StringQuery(@NonNull Class table, @NonNull String sql) { - super(table); - query = sql; - } - - @Override - public String getQuery() { - return query; - } - - @Override - public FlowCursor query() { - return query(FlowManager.getDatabaseForTable(getTable()).getWritableDatabase()); - } - - @Override - public FlowCursor query(@NonNull DatabaseWrapper databaseWrapper) { - return databaseWrapper.rawQuery(query, args); - } - - /** - * Set selection arguments to execute on this raw query. - */ - @NonNull - public StringQuery setSelectionArgs(@NonNull String[] args) { - this.args = args; - return this; - } - - @NonNull - @Override - public BaseModel.Action getPrimaryAction() { - return BaseModel.Action.CHANGE; // we don't explicitly know the change, but something changed. - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/AutoIncrementModelSaver.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/AutoIncrementModelSaver.java deleted file mode 100644 index 43d7c1dd6..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/AutoIncrementModelSaver.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.raizlabs.android.dbflow.sql.saveable; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.runtime.NotifyDistributor; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Used to properly handle autoincrementing fields. - */ -public class AutoIncrementModelSaver extends ModelSaver { - - @Override - public synchronized long insert(@NonNull TModel model) { - return insert(model, getWritableDatabase()); - } - - @Override - public synchronized long insert(@NonNull TModel model, @NonNull DatabaseWrapper wrapper) { - final boolean hasAutoIncrement = getModelAdapter().hasAutoIncrement(model); - DatabaseStatement insertStatement = hasAutoIncrement - ? getModelAdapter().getCompiledStatement(wrapper) - : getModelAdapter().getInsertStatement(wrapper); - long id; - try { - getModelAdapter().saveForeignKeys(model, wrapper); - if (hasAutoIncrement) { - getModelAdapter().bindToStatement(insertStatement, model); - } else { - getModelAdapter().bindToInsertStatement(insertStatement, model); - } - id = insertStatement.executeInsert(); - if (id > INSERT_FAILED) { - getModelAdapter().updateAutoIncrement(model, id); - NotifyDistributor.get().notifyModelChanged(model, getModelAdapter(), BaseModel.Action.INSERT); - } - } finally { - // since we generate an insert every time, we can safely close the statement here. - insertStatement.close(); - } - return id; - } - - @Override - public synchronized long insert(@NonNull TModel model, - @NonNull DatabaseStatement insertStatement, - @NonNull DatabaseWrapper wrapper) { - if (!getModelAdapter().hasAutoIncrement(model)) { - return super.insert(model, insertStatement, wrapper); - } else { - FlowLog.log(FlowLog.Level.W, "Ignoring insert statement " + insertStatement + " since an autoincrement column specified in the insert."); - return insert(model, wrapper); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/CacheableListModelSaver.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/CacheableListModelSaver.java deleted file mode 100644 index c44478cd9..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/CacheableListModelSaver.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.raizlabs.android.dbflow.sql.saveable; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.Collection; - -/** - * Description: Used for model caching, enables caching models when saving in list. - */ -public class CacheableListModelSaver - extends ListModelSaver { - - public CacheableListModelSaver(@NonNull ModelSaver modelSaver) { - super(modelSaver); - } - - @Override - public synchronized void saveAll(@NonNull Collection tableCollection, - @NonNull DatabaseWrapper wrapper) { - // skip if empty. - if (tableCollection.isEmpty()) { - return; - } - - ModelSaver modelSaver = getModelSaver(); - ModelAdapter modelAdapter = modelSaver.getModelAdapter(); - DatabaseStatement statement = modelAdapter.getInsertStatement(wrapper); - DatabaseStatement updateStatement = modelAdapter.getUpdateStatement(wrapper); - try { - for (TModel model : tableCollection) { - if (modelSaver.save(model, wrapper, statement, updateStatement)) { - modelAdapter.storeModelInCache(model); - } - } - } finally { - updateStatement.close(); - statement.close(); - } - } - - @Override - public synchronized void insertAll(@NonNull Collection tableCollection, - @NonNull DatabaseWrapper wrapper) { - // skip if empty. - if (tableCollection.isEmpty()) { - return; - } - - ModelSaver modelSaver = getModelSaver(); - ModelAdapter modelAdapter = modelSaver.getModelAdapter(); - DatabaseStatement statement = modelAdapter.getInsertStatement(wrapper); - try { - for (TModel model : tableCollection) { - if (modelSaver.insert(model, statement, wrapper) > 0) { - modelAdapter.storeModelInCache(model); - } - } - } finally { - statement.close(); - } - } - - @Override - public synchronized void updateAll(@NonNull Collection tableCollection, - @NonNull DatabaseWrapper wrapper) { - // skip if empty. - if (tableCollection.isEmpty()) { - return; - } - ModelSaver modelSaver = getModelSaver(); - ModelAdapter modelAdapter = modelSaver.getModelAdapter(); - DatabaseStatement statement = modelAdapter.getUpdateStatement(wrapper); - try { - for (TModel model : tableCollection) { - if (modelSaver.update(model, wrapper, statement)) { - modelAdapter.storeModelInCache(model); - } - } - } finally { - statement.close(); - } - } - - @Override - public synchronized void deleteAll(@NonNull Collection tableCollection, - @NonNull DatabaseWrapper wrapper) { - // skip if empty. - if (tableCollection.isEmpty()) { - return; - } - - ModelSaver modelSaver = getModelSaver(); - for (TModel model : tableCollection) { - if (modelSaver.delete(model, wrapper)) { - getModelSaver().getModelAdapter().removeModelFromCache(model); - } - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/ListModelSaver.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/ListModelSaver.java deleted file mode 100644 index 2c10e30cd..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/ListModelSaver.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.raizlabs.android.dbflow.sql.saveable; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.Collection; - -public class ListModelSaver { - - private final ModelSaver modelSaver; - - public ListModelSaver(@NonNull ModelSaver modelSaver) { - this.modelSaver = modelSaver; - } - - public synchronized void saveAll(@NonNull Collection tableCollection) { - saveAll(tableCollection, modelSaver.getWritableDatabase()); - } - - public synchronized void saveAll(@NonNull Collection tableCollection, - @NonNull DatabaseWrapper wrapper) { - // skip if empty. - if (tableCollection.isEmpty()) { - return; - } - - DatabaseStatement statement = modelSaver.getModelAdapter().getInsertStatement(wrapper); - DatabaseStatement updateStatement = modelSaver.getModelAdapter().getUpdateStatement(wrapper); - try { - for (TModel model : tableCollection) { - modelSaver.save(model, wrapper, statement, updateStatement); - } - } finally { - statement.close(); - updateStatement.close(); - } - } - - public synchronized void insertAll(@NonNull Collection tableCollection) { - insertAll(tableCollection, modelSaver.getWritableDatabase()); - } - - public synchronized void insertAll(@NonNull Collection tableCollection, - @NonNull DatabaseWrapper wrapper) { - // skip if empty. - if (tableCollection.isEmpty()) { - return; - } - - DatabaseStatement statement = modelSaver.getModelAdapter().getInsertStatement(wrapper); - try { - for (TModel model : tableCollection) { - modelSaver.insert(model, statement, wrapper); - } - } finally { - statement.close(); - } - } - - public synchronized void updateAll(@NonNull Collection tableCollection) { - updateAll(tableCollection, modelSaver.getWritableDatabase()); - } - - public synchronized void updateAll(@NonNull Collection tableCollection, - @NonNull DatabaseWrapper wrapper) { - // skip if empty. - if (tableCollection.isEmpty()) { - return; - } - - DatabaseStatement updateStatement = modelSaver.getModelAdapter().getUpdateStatement(wrapper); - try { - for (TModel model : tableCollection) { - modelSaver.update(model, wrapper, updateStatement); - } - } finally { - updateStatement.close(); - } - } - - public synchronized void deleteAll(@NonNull Collection tableCollection) { - deleteAll(tableCollection, modelSaver.getWritableDatabase()); - } - - public synchronized void deleteAll(@NonNull Collection tableCollection, - @NonNull DatabaseWrapper wrapper) { - // skip if empty. - if (tableCollection.isEmpty()) { - return; - } - - DatabaseStatement deleteStatement = modelSaver.getModelAdapter().getDeleteStatement(wrapper); - try { - for (TModel model : tableCollection) { - modelSaver.delete(model, deleteStatement, wrapper); - } - } finally { - deleteStatement.close(); - } - } - - @NonNull - public ModelSaver getModelSaver() { - return modelSaver; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/ModelSaver.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/ModelSaver.java deleted file mode 100644 index e19c828e8..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/saveable/ModelSaver.java +++ /dev/null @@ -1,225 +0,0 @@ -package com.raizlabs.android.dbflow.sql.saveable; - -import android.content.ContentValues; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.ConflictAction; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.runtime.NotifyDistributor; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Defines how models get saved into the DB. It will bind values to {@link DatabaseStatement} - * for all CRUD operations as they are wildly faster and more efficient than {@link ContentValues}. - */ -public class ModelSaver { - - public static final int INSERT_FAILED = -1; - - private ModelAdapter modelAdapter; - - public void setModelAdapter(@NonNull ModelAdapter modelAdapter) { - this.modelAdapter = modelAdapter; - } - - public synchronized boolean save(@NonNull TModel model) { - return save(model, getWritableDatabase(), modelAdapter.getInsertStatement(), - modelAdapter.getUpdateStatement()); - } - - public synchronized boolean save(@NonNull TModel model, @NonNull DatabaseWrapper wrapper) { - boolean exists = getModelAdapter().exists(model, wrapper); - - if (exists) { - exists = update(model, wrapper); - } - - if (!exists) { - exists = insert(model, wrapper) > INSERT_FAILED; - } - - if (exists) { - NotifyDistributor.get().notifyModelChanged(model, getModelAdapter(), BaseModel.Action.SAVE); - } - - // return successful store into db. - return exists; - } - - @SuppressWarnings("unchecked") - public synchronized boolean save(@NonNull TModel model, - @NonNull DatabaseWrapper wrapper, - @NonNull DatabaseStatement insertStatement, - @NonNull DatabaseStatement updateStatement) { - boolean exists = modelAdapter.exists(model, wrapper); - - if (exists) { - exists = update(model, wrapper, updateStatement); - } - - if (!exists) { - exists = insert(model, insertStatement, wrapper) > INSERT_FAILED; - } - - if (exists) { - NotifyDistributor.get().notifyModelChanged(model, modelAdapter, BaseModel.Action.SAVE); - } - - // return successful store into db. - return exists; - } - - public synchronized boolean update(@NonNull TModel model) { - return update(model, getWritableDatabase(), modelAdapter.getUpdateStatement()); - } - - public synchronized boolean update(@NonNull TModel model, @NonNull DatabaseWrapper wrapper) { - DatabaseStatement updateStatement = modelAdapter.getUpdateStatement(wrapper); - boolean success = false; - try { - success = update(model, wrapper, updateStatement); - } finally { - // since we generate an insert every time, we can safely close the statement here. - updateStatement.close(); - } - return success; - } - - @SuppressWarnings("unchecked") - public synchronized boolean update(@NonNull TModel model, @NonNull DatabaseWrapper wrapper, - @NonNull DatabaseStatement databaseStatement) { - modelAdapter.saveForeignKeys(model, wrapper); - modelAdapter.bindToUpdateStatement(databaseStatement, model); - boolean successful = databaseStatement.executeUpdateDelete() != 0; - if (successful) { - NotifyDistributor.get().notifyModelChanged(model, modelAdapter, BaseModel.Action.UPDATE); - } - return successful; - } - - @SuppressWarnings("unchecked") - public synchronized long insert(@NonNull TModel model) { - return insert(model, modelAdapter.getInsertStatement(), getWritableDatabase()); - } - - @SuppressWarnings("unchecked") - public synchronized long insert(@NonNull TModel model, @NonNull DatabaseWrapper wrapper) { - DatabaseStatement insertStatement = modelAdapter.getInsertStatement(wrapper); - long result = 0; - try { - result = insert(model, insertStatement, wrapper); - } finally { - // since we generate an insert every time, we can safely close the statement here. - insertStatement.close(); - } - return result; - } - - @SuppressWarnings("unchecked") - public synchronized long insert(@NonNull TModel model, - @NonNull DatabaseStatement insertStatement, - @NonNull DatabaseWrapper wrapper) { - modelAdapter.saveForeignKeys(model, wrapper); - modelAdapter.bindToInsertStatement(insertStatement, model); - long id = insertStatement.executeInsert(); - if (id > INSERT_FAILED) { - modelAdapter.updateAutoIncrement(model, id); - NotifyDistributor.get().notifyModelChanged(model, modelAdapter, BaseModel.Action.INSERT); - } - return id; - } - - public synchronized boolean delete(@NonNull TModel model) { - return delete(model, modelAdapter.getDeleteStatement(), getWritableDatabase()); - } - - @SuppressWarnings("unchecked") - public synchronized boolean delete(@NonNull TModel model, @NonNull DatabaseWrapper wrapper) { - DatabaseStatement deleteStatement = modelAdapter.getDeleteStatement(wrapper); - boolean success = false; - try { - success = delete(model, deleteStatement, wrapper); - } finally { - // since we generate an insert every time, we can safely close the statement here. - deleteStatement.close(); - } - return success; - } - - @SuppressWarnings("unchecked") - public synchronized boolean delete(@NonNull TModel model, - @NonNull DatabaseStatement deleteStatement, - @NonNull DatabaseWrapper wrapper) { - modelAdapter.deleteForeignKeys(model, wrapper); - modelAdapter.bindToDeleteStatement(deleteStatement, model); - - boolean success = deleteStatement.executeUpdateDelete() != 0; - if (success) { - NotifyDistributor.get().notifyModelChanged(model, modelAdapter, BaseModel.Action.DELETE); - } - modelAdapter.updateAutoIncrement(model, 0); - return success; - } - - @NonNull - protected DatabaseWrapper getWritableDatabase() { - return FlowManager.getDatabaseForTable(modelAdapter.getModelClass()).getWritableDatabase(); - } - - @NonNull - public ModelAdapter getModelAdapter() { - return modelAdapter; - } - - /** - * Legacy save method. Uses {@link ContentValues} vs. the faster {@link DatabaseStatement} for updates. - * - * @see #save(Object, DatabaseWrapper, DatabaseStatement, DatabaseStatement) - */ - @Deprecated - @SuppressWarnings({"unchecked", "deprecation"}) - public synchronized boolean save(@NonNull TModel model, - @NonNull DatabaseWrapper wrapper, - @NonNull DatabaseStatement insertStatement, - @NonNull ContentValues contentValues) { - boolean exists = modelAdapter.exists(model, wrapper); - - if (exists) { - exists = update(model, wrapper, contentValues); - } - - if (!exists) { - exists = insert(model, insertStatement, wrapper) > INSERT_FAILED; - } - - if (exists) { - NotifyDistributor.get().notifyModelChanged(model, modelAdapter, BaseModel.Action.SAVE); - } - - // return successful store into db. - return exists; - } - - /** - * @see #update(Object, DatabaseWrapper, DatabaseStatement) - */ - @Deprecated - @SuppressWarnings("unchecked") - public synchronized boolean update(@NonNull TModel model, - @NonNull DatabaseWrapper wrapper, - @NonNull ContentValues contentValues) { - modelAdapter.saveForeignKeys(model, wrapper); - modelAdapter.bindToContentValues(contentValues, model); - boolean successful = wrapper.updateWithOnConflict(modelAdapter.getTableName(), contentValues, - modelAdapter.getPrimaryConditionClause(model).getQuery(), null, - ConflictAction.getSQLiteDatabaseAlgorithmInt(modelAdapter.getUpdateOnConflictAction())) != 0; - if (successful) { - NotifyDistributor.get().notifyModelChanged(model, modelAdapter, BaseModel.Action.UPDATE); - } - return successful; - } -} - diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/AsyncModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/AsyncModel.java deleted file mode 100644 index a477d8b5f..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/AsyncModel.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.BaseAsyncObject; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.transaction.DefaultTransactionQueue; -import com.raizlabs.android.dbflow.structure.database.transaction.ProcessModelTransaction; -import com.raizlabs.android.dbflow.structure.database.transaction.Transaction; - -import java.lang.ref.WeakReference; - -/** - * Description: Called from a {@link BaseModel}, this places the current {@link Model} interaction on the background. - */ -public class AsyncModel extends BaseAsyncObject> implements Model { - - /** - * Listens for when this {@link Model} modification completes. - */ - public interface OnModelChangedListener { - - /** - * Called when the change finishes on the {@link DefaultTransactionQueue}. This method is called on the UI thread. - */ - void onModelChanged(@NonNull T model); - } - - private final TModel model; - private transient WeakReference> onModelChangedListener; - - private ModelAdapter modelAdapter; - - public AsyncModel(@NonNull TModel referenceModel) { - super(referenceModel.getClass()); - model = referenceModel; - } - - /** - * Call before {@link #save()}, {@link #delete()}, {@link #update()}, or {@link #insert()}. - * - * @param onModelChangedListener The listener to use for a corresponding call to a method. - */ - public AsyncModel withListener(@Nullable OnModelChangedListener onModelChangedListener) { - this.onModelChangedListener = new WeakReference<>(onModelChangedListener); - return this; - } - - private ModelAdapter getModelAdapter() { - if (modelAdapter == null) { - //noinspection unchecked - modelAdapter = (ModelAdapter) FlowManager.getModelAdapter(model.getClass()); - } - return modelAdapter; - } - - @Override - public boolean save(@NonNull DatabaseWrapper wrapper) { - return save(); - } - - @Override - public boolean save() { - executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().save(model, wrapper); - } - }).add(model).build()); - return false; - } - - @Override - public boolean delete(@NonNull DatabaseWrapper wrapper) { - return delete(); - } - - @Override - public boolean delete() { - executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().delete(model, wrapper); - } - }).add(model).build()); - return false; - } - - @Override - public boolean update(@NonNull DatabaseWrapper wrapper) { - return update(); - } - - @Override - public boolean update() { - executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().update(model, wrapper); - } - }).add(model).build()); - return false; - } - - @Override - public long insert(DatabaseWrapper wrapper) { - return insert(); - } - - @Override - public long insert() { - executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().insert(model, wrapper); - } - }).add(model).build()); - return INVALID_ROW_ID; - } - - @Override - public void load(@NonNull DatabaseWrapper wrapper) { - load(); - } - - @Override - public void load() { - executeTransaction(new ProcessModelTransaction.Builder<>( - new ProcessModelTransaction.ProcessModel() { - @Override - public void processModel(TModel model, DatabaseWrapper wrapper) { - getModelAdapter().load(model, wrapper); - } - }).add(model).build()); - } - - @Override - public boolean exists(@NonNull DatabaseWrapper wrapper) { - return exists(); - } - - @Override - public boolean exists() { - return getModelAdapter().exists(model); - } - - /** - * @return Itself since it's already async. - */ - @NonNull - @Override - public AsyncModel async() { - //noinspection unchecked - return (AsyncModel) this; - } - - @Override - protected void onSuccess(@NonNull Transaction transaction) { - if (onModelChangedListener != null && onModelChangedListener.get() != null) { - onModelChangedListener.get().onModelChanged(model); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java deleted file mode 100644 index 1deeaa793..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModel.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.ColumnIgnore; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: The base implementation of {@link Model}. It is recommended to use this class as - * the base for your {@link Model}, but it is not required. - */ -@SuppressWarnings("unchecked") -public class BaseModel implements Model { - - /** - * Specifies the Action that was taken when data changes - */ - public enum Action { - - /** - * The model called {@link Model#save()} - */ - SAVE, - - /** - * The model called {@link Model#insert()} - */ - INSERT, - - /** - * The model called {@link Model#update()} - */ - UPDATE, - - /** - * The model called {@link Model#delete()} - */ - DELETE, - - /** - * The model was changed. used in prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - CHANGE - } - - @ColumnIgnore - private transient ModelAdapter modelAdapter; - - @Override - public void load() { - getModelAdapter().load(this); - } - - @Override - public void load(@NonNull DatabaseWrapper wrapper) { - getModelAdapter().load(this, wrapper); - } - - @Override - public boolean save() { - return getModelAdapter().save(this); - } - - - @Override - public boolean save(@NonNull DatabaseWrapper databaseWrapper) { - return getModelAdapter().save(this, databaseWrapper); - } - - @Override - public boolean delete() { - return getModelAdapter().delete(this); - } - - @Override - public boolean delete(@NonNull DatabaseWrapper databaseWrapper) { - return getModelAdapter().delete(this, databaseWrapper); - } - - @Override - public boolean update() { - return getModelAdapter().update(this); - } - - @Override - public boolean update(@NonNull DatabaseWrapper databaseWrapper) { - return getModelAdapter().update(this, databaseWrapper); - } - - @Override - public long insert() { - return getModelAdapter().insert(this); - } - - @Override - public long insert(DatabaseWrapper databaseWrapper) { - return getModelAdapter().insert(this, databaseWrapper); - } - - @Override - public boolean exists() { - return getModelAdapter().exists(this); - } - - @Override - public boolean exists(@NonNull DatabaseWrapper databaseWrapper) { - return getModelAdapter().exists(this, databaseWrapper); - } - - @NonNull - @Override - public AsyncModel async() { - return new AsyncModel<>(this); - } - - /** - * @return The associated {@link ModelAdapter}. The {@link FlowManager} - * may throw a {@link InvalidDBConfiguration} for this call if this class - * is not associated with a table, so be careful when using this method. - */ - public ModelAdapter getModelAdapter() { - if (modelAdapter == null) { - modelAdapter = FlowManager.getModelAdapter(getClass()); - } - return modelAdapter; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModelView.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModelView.java deleted file mode 100644 index 708640729..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseModelView.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import com.raizlabs.android.dbflow.annotation.ModelView; -import com.raizlabs.android.dbflow.annotation.ModelViewQuery; - -/** - * Description: Provides a base implementation for a ModelView. Use a {@link ModelView} - * annotation to register it properly. Also you need to specify a singular - * field via {@link ModelViewQuery}. - */ -public abstract class BaseModelView extends NoModificationModel { - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseQueryModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseQueryModel.java deleted file mode 100644 index 36333d667..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/BaseQueryModel.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.QueryModel; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Provides a base class for objects that represent {@link QueryModel}. - */ -public class BaseQueryModel extends NoModificationModel { - - @Override - public boolean exists() { - throw new InvalidSqlViewOperationException("Query " + getClass().getName() + - " does not exist as a table." + "It's a convenient representation of a complex SQLite query."); - } - - @Override - public boolean exists(@NonNull DatabaseWrapper databaseWrapper) { - return exists(); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InstanceAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InstanceAdapter.java deleted file mode 100644 index 4be57ecb5..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InstanceAdapter.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; - -/** - * Description: Provides a {@link #newInstance()} method to a {@link RetrievalAdapter} - */ -@SuppressWarnings("NullableProblems") -public abstract class InstanceAdapter - extends RetrievalAdapter { - - public InstanceAdapter(@NonNull DatabaseDefinition databaseDefinition) { - super(databaseDefinition); - } - - /** - * @return A new model using its default constructor. This is why default is required so that - * we don't use reflection to create objects = faster. - */ - @NonNull - public abstract TModel newInstance(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java deleted file mode 100644 index 064414487..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InternalAdapter.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.content.ContentValues; -import android.database.sqlite.SQLiteStatement; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.Collection; - -/** - * Description: Used for our internal Adapter classes such as generated {@link ModelAdapter}. - */ -@SuppressWarnings("NullableProblems") -public interface InternalAdapter { - - /** - * @return The table name of this adapter. - */ - @NonNull - String getTableName(); - - /** - * Saves the specified model to the DB. - * - * @param model The model to save/insert/update - */ - boolean save(@NonNull TModel model); - - /** - * Saves the specified model to the DB. - * - * @param model The model to save/insert/update - * @param databaseWrapper The manually specified wrapper. - */ - boolean save(@NonNull TModel model, @NonNull DatabaseWrapper databaseWrapper); - - /** - * Saves a {@link Collection} of models to the DB. - * - * @param models The {@link Collection} of models to save. - */ - void saveAll(@NonNull Collection models); - - /** - * Saves a {@link Collection} of models to the DB. - * - * @param models The {@link Collection} of models to save. - * @param databaseWrapper The manually specified wrapper - */ - void saveAll(@NonNull Collection models, @NonNull DatabaseWrapper databaseWrapper); - - /** - * Inserts the specified model into the DB. - * - * @param model The model to insert. - */ - long insert(@NonNull TModel model); - - /** - * Inserts the specified model into the DB. - * - * @param model The model to insert. - * @param databaseWrapper The manually specified wrapper. - */ - long insert(@NonNull TModel model, @NonNull DatabaseWrapper databaseWrapper); - - /** - * Inserts a {@link Collection} of models into the DB. - * - * @param models The {@link Collection} of models to save. - */ - void insertAll(@NonNull Collection models); - - /** - * Inserts a {@link Collection} of models into the DB. - * - * @param models The {@link Collection} of models to save. - * @param databaseWrapper The manually specified wrapper - */ - void insertAll(@NonNull Collection models, @NonNull DatabaseWrapper databaseWrapper); - - /** - * Updates the specified model into the DB. - * - * @param model The model to update. - */ - boolean update(@NonNull TModel model); - - /** - * Updates the specified model into the DB. - * - * @param model The model to update. - * @param databaseWrapper The manually specified wrapper. - */ - boolean update(@NonNull TModel model, @NonNull DatabaseWrapper databaseWrapper); - - /** - * Updates a {@link Collection} of models in the DB. - * - * @param models The {@link Collection} of models to save. - */ - void updateAll(@NonNull Collection models); - - /** - * Updates a {@link Collection} of models in the DB. - * - * @param models The {@link Collection} of models to save. - * @param databaseWrapper The manually specified wrapper - */ - void updateAll(@NonNull Collection models, @NonNull DatabaseWrapper databaseWrapper); - - /** - * Deletes the model from the DB - * - * @param model The model to delete - */ - boolean delete(@NonNull TModel model); - - /** - * Deletes the model from the DB - * - * @param model The model to delete - * @param databaseWrapper The manually specified wrapper. - */ - boolean delete(@NonNull TModel model, @NonNull DatabaseWrapper databaseWrapper); - - /** - * Updates a {@link Collection} of models in the DB. - * - * @param models The {@link Collection} of models to save. - */ - void deleteAll(@NonNull Collection models); - - /** - * Updates a {@link Collection} of models in the DB. - * - * @param models The {@link Collection} of models to save. - * @param databaseWrapper The manually specified wrapper - */ - void deleteAll(@NonNull Collection models, @NonNull DatabaseWrapper databaseWrapper); - - /** - * Binds a {@link TModel} to the specified db statement - * - * @param sqLiteStatement The statement to fill - */ - void bindToStatement(@NonNull DatabaseStatement sqLiteStatement, @NonNull TModel model); - - /** - * Provides common logic and a starting value for insert statements. So we can property compile - * and bind statements without generating duplicate code. - * - * @param sqLiteStatement The statement to bind. - * @param model The model to retrieve data from. - * @param start The starting index for this bind. - */ - void bindToInsertStatement(@NonNull DatabaseStatement sqLiteStatement, @NonNull TModel model, - @IntRange(from = 0, to = 1) int start); - - /** - * Binds to a {@link SQLiteStatement}. It leaves out an autoincrementing primary key (if specified) - * to keep the true nature of AI keys. - * - * @param sqLiteStatement The statement to bind to. - * @param model The model to read from. - */ - void bindToInsertStatement(@NonNull DatabaseStatement sqLiteStatement, @NonNull TModel model); - - /** - * Binds a {@link TModel} to the specified db statement - * - * @param contentValues The content values to fill. - * @param model The model values to put on the contentvalues - */ - void bindToContentValues(@NonNull ContentValues contentValues, @NonNull TModel model); - - /** - * Binds a {@link TModel} to the specified db statement, leaving out the {@link PrimaryKey#autoincrement()} - * column. - * - * @param contentValues The content values to fill. - * @param model The model values to put on the content values. - */ - void bindToInsertValues(@NonNull ContentValues contentValues, @NonNull TModel model); - - /** - * Binds values of the model to an update {@link DatabaseStatement}. It repeats each primary - * key binding twice to ensure proper update statements. - */ - void bindToUpdateStatement(@NonNull DatabaseStatement updateStatement, @NonNull TModel model); - - /** - * Binds values of the model to a delete {@link DatabaseStatement}. - */ - void bindToDeleteStatement(@NonNull DatabaseStatement deleteStatement, @NonNull TModel model); - - /** - * If a model has an autoincrementing primary key, then - * this method will be overridden. - * - * @param model The model object to store the key - * @param id The key to store - */ - void updateAutoIncrement(@NonNull TModel model, @NonNull Number id); - - /** - * @return The value for the {@link PrimaryKey#autoincrement()} - * if it has the field. This method is overridden when its specified for the {@link TModel} - */ - @Nullable - Number getAutoIncrementingId(@NonNull TModel model); - - /** - * @return true if the {@link InternalAdapter} can be cached. - */ - boolean cachingEnabled(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InvalidDBConfiguration.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InvalidDBConfiguration.java deleted file mode 100644 index c863951b7..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/InvalidDBConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -/** - * Description: Thrown when a DB is incorrectly configured. - */ -public class InvalidDBConfiguration extends RuntimeException { - - public InvalidDBConfiguration() { - super("No Databases were found. Did you create a Database Annotation placeholder class?"); - } - - public InvalidDBConfiguration(String message) { - super(message); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/Model.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/Model.java deleted file mode 100644 index 12ea1a6f3..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/Model.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.transaction.DefaultTransactionQueue; - -public interface Model extends ReadOnlyModel { - - /** - * Returned when {@link #insert()} occurs in an async state or some kind of issue occurs. - */ - long INVALID_ROW_ID = -1; - - /** - * Saves the object in the DB. - * - * @return true if successful - */ - boolean save(); - - /** - * Saves the object in the DB. - * - * @return true if successful - */ - boolean save(@NonNull DatabaseWrapper wrapper); - - /** - * Deletes the object in the DB - * - * @return true if successful - */ - boolean delete(); - - /** - * Deletes the object in the DB - * - * @return true if successful - */ - boolean delete(@NonNull DatabaseWrapper wrapper); - - /** - * Updates an object in the DB. Does not insert on failure. - * - * @return true if successful - */ - boolean update(); - - /** - * Updates an object in the DB. Does not insert on failure. - * - * @return true if successful - */ - boolean update(@NonNull DatabaseWrapper wrapper); - - /** - * Inserts the object into the DB - * - * @return the count of the rows affected, should only be 1 here, or -1 if failed. - */ - long insert(); - - /** - * Inserts the object into the DB - * - * @return the count of the rows affected, should only be 1 here, or -1 if failed. - */ - long insert(DatabaseWrapper wrapper); - - /** - * @return An async instance of this model where all transactions are on the {@link DefaultTransactionQueue} - */ - @NonNull - AsyncModel async(); - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java deleted file mode 100644 index fe603a977..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelAdapter.java +++ /dev/null @@ -1,564 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.content.ContentValues; -import android.database.sqlite.SQLiteStatement; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.ConflictAction; -import com.raizlabs.android.dbflow.annotation.ForeignKey; -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.annotation.Table; -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.sql.language.property.IProperty; -import com.raizlabs.android.dbflow.sql.language.property.Property; -import com.raizlabs.android.dbflow.sql.saveable.ListModelSaver; -import com.raizlabs.android.dbflow.sql.saveable.ModelSaver; -import com.raizlabs.android.dbflow.structure.cache.IMultiKeyCacheConverter; -import com.raizlabs.android.dbflow.structure.cache.ModelCache; -import com.raizlabs.android.dbflow.structure.cache.SimpleMapCache; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.Collection; - -import static com.raizlabs.android.dbflow.config.FlowManager.getWritableDatabaseForTable; - -/** - * Description: Used for generated classes from the combination of {@link Table} and {@link Model}. - */ -@SuppressWarnings("NullableProblems") -public abstract class ModelAdapter extends InstanceAdapter - implements InternalAdapter { - - private DatabaseStatement insertStatement; - private DatabaseStatement compiledStatement; - private DatabaseStatement updateStatement; - private DatabaseStatement deleteStatement; - - private String[] cachingColumns; - private ModelCache modelCache; - private ModelSaver modelSaver; - private ListModelSaver listModelSaver; - - public ModelAdapter(@NonNull DatabaseDefinition databaseDefinition) { - super(databaseDefinition); - if (getTableConfig() != null && getTableConfig().modelSaver() != null) { - modelSaver = getTableConfig().modelSaver(); - modelSaver.setModelAdapter(this); - } - } - - /** - * @return The pre-compiled insert statement for this table model adapter. This is reused and cached. - */ - @NonNull - public DatabaseStatement getInsertStatement() { - if (insertStatement == null) { - insertStatement = getInsertStatement(getWritableDatabaseForTable(getModelClass())); - } - - return insertStatement; - } - - /** - * @return The pre-compiled update statement for this table model adapter. This is reused and cached. - */ - @NonNull - public DatabaseStatement getUpdateStatement() { - if (updateStatement == null) { - updateStatement = getUpdateStatement(getWritableDatabaseForTable(getModelClass())); - } - - return updateStatement; - } - - /** - * @return The pre-compiled delete statement for this table model adapter. This is reused and cached. - */ - @NonNull - public DatabaseStatement getDeleteStatement() { - if (deleteStatement == null) { - deleteStatement = getDeleteStatement(getWritableDatabaseForTable(getModelClass())); - } - - return deleteStatement; - } - - public void closeInsertStatement() { - if (insertStatement == null) { - return; - } - insertStatement.close(); - insertStatement = null; - } - - public void closeUpdateStatement() { - if (updateStatement == null) { - return; - } - updateStatement.close(); - updateStatement = null; - } - - public void closeDeleteStatement() { - if (deleteStatement == null) { - return; - } - deleteStatement.close(); - deleteStatement = null; - } - - /** - * @param databaseWrapper The database used to do an insert statement. - * @return a new compiled {@link DatabaseStatement} representing insert. Not cached, always generated. - * To bind values use {@link #bindToInsertStatement(DatabaseStatement, Object)}. - */ - @NonNull - public DatabaseStatement getInsertStatement(@NonNull DatabaseWrapper databaseWrapper) { - return databaseWrapper.compileStatement(getInsertStatementQuery()); - } - - /** - * @param databaseWrapper The database used to do an update statement. - * @return a new compiled {@link DatabaseStatement} representing update. Not cached, always generated. - * To bind values use {@link #bindToUpdateStatement(DatabaseStatement, Object)}. - */ - @NonNull - public DatabaseStatement getUpdateStatement(@NonNull DatabaseWrapper databaseWrapper) { - return databaseWrapper.compileStatement(getUpdateStatementQuery()); - } - - /** - * @param databaseWrapper The database used to do a delete statement. - * @return a new compiled {@link DatabaseStatement} representing delete. Not cached, always generated. - * To bind values use {@link #bindToDeleteStatement(DatabaseStatement, Object)}. - */ - @NonNull - public DatabaseStatement getDeleteStatement(@NonNull DatabaseWrapper databaseWrapper) { - return databaseWrapper.compileStatement(getDeleteStatementQuery()); - } - - /** - * @return The precompiled full statement for this table model adapter - */ - @NonNull - public DatabaseStatement getCompiledStatement() { - if (compiledStatement == null) { - compiledStatement = getCompiledStatement(getWritableDatabaseForTable(getModelClass())); - } - - return compiledStatement; - } - - public void closeCompiledStatement() { - if (compiledStatement == null) { - return; - } - compiledStatement.close(); - compiledStatement = null; - } - - /** - * @param databaseWrapper The database used to do an insert statement. - * @return a new compiled {@link DatabaseStatement} representing insert. - * To bind values use {@link #bindToInsertStatement(DatabaseStatement, Object)}. - */ - public DatabaseStatement getCompiledStatement(@NonNull DatabaseWrapper databaseWrapper) { - return databaseWrapper.compileStatement(getCompiledStatementQuery()); - } - - /** - * Creates a new {@link TModel} and Loads the cursor into a the object. - * - * @param cursor The cursor to load - * @return A new {@link TModel} - */ - public TModel loadFromCursor(@NonNull FlowCursor cursor) { - TModel model = newInstance(); - loadFromCursor(cursor, model); - return model; - } - - @Override - public boolean save(@NonNull TModel model) { - return getModelSaver().save(model); - } - - @Override - public boolean save(@NonNull TModel model, @NonNull DatabaseWrapper databaseWrapper) { - return getModelSaver().save(model, databaseWrapper); - } - - @Override - public void saveAll(@NonNull Collection models) { - getListModelSaver().saveAll(models); - } - - @Override - public void saveAll(@NonNull Collection models, @NonNull DatabaseWrapper databaseWrapper) { - getListModelSaver().saveAll(models, databaseWrapper); - } - - @Override - public long insert(@NonNull TModel model) { - return getModelSaver().insert(model); - } - - @Override - public long insert(@NonNull TModel model, @NonNull DatabaseWrapper databaseWrapper) { - return getModelSaver().insert(model, databaseWrapper); - } - - @Override - public void insertAll(@NonNull Collection models) { - getListModelSaver().insertAll(models); - } - - @Override - public void insertAll(@NonNull Collection models, @NonNull DatabaseWrapper databaseWrapper) { - getListModelSaver().insertAll(models, databaseWrapper); - } - - @Override - public boolean update(@NonNull TModel model) { - return getModelSaver().update(model); - } - - @Override - public boolean update(@NonNull TModel model, @NonNull DatabaseWrapper databaseWrapper) { - return getModelSaver().update(model, databaseWrapper); - } - - @Override - public void updateAll(@NonNull Collection models) { - getListModelSaver().updateAll(models); - } - - @Override - public void updateAll(@NonNull Collection models, @NonNull DatabaseWrapper databaseWrapper) { - getListModelSaver().updateAll(models, databaseWrapper); - } - - @Override - public boolean delete(@NonNull TModel model) { - return getModelSaver().delete(model); - } - - @Override - public boolean delete(@NonNull TModel model, @NonNull DatabaseWrapper databaseWrapper) { - return getModelSaver().delete(model, databaseWrapper); - } - - @Override - public void deleteAll(@NonNull Collection tModels, @NonNull DatabaseWrapper databaseWrapper) { - getListModelSaver().deleteAll(tModels, databaseWrapper); - } - - @Override - public void deleteAll(@NonNull Collection tModels) { - getListModelSaver().deleteAll(tModels); - } - - @Override - public void bindToInsertStatement(@NonNull DatabaseStatement sqLiteStatement, @NonNull TModel model) { - bindToInsertStatement(sqLiteStatement, model, 0); - } - - @Override - public void bindToContentValues(@NonNull ContentValues contentValues, @NonNull TModel tModel) { - bindToInsertValues(contentValues, tModel); - } - - @Override - public void bindToStatement(@NonNull DatabaseStatement sqLiteStatement, @NonNull TModel tModel) { - bindToInsertStatement(sqLiteStatement, tModel, 0); - } - - /** - * If a {@link Model} has an auto-incrementing primary key, then - * this method will be overridden. - * - * @param model The model object to store the key - * @param id The key to store - */ - @Override - public void updateAutoIncrement(@NonNull TModel model, @NonNull Number id) { - - } - - /** - * @return The value for the {@link PrimaryKey#autoincrement()} - * if it has the field. This method is overridden when its specified for the {@link TModel} - */ - @Nullable - @Override - public Number getAutoIncrementingId(@NonNull TModel model) { - throw new InvalidDBConfiguration( - String.format("This method may have been called in error. The model class %1s must contain" + - "a single primary key (if used in a ModelCache, this method may be called)", - getModelClass())); - } - - /** - * @return The autoincrement column name for the {@link PrimaryKey#autoincrement()} - * if it has the field. This method is overridden when its specified for the {@link TModel} - */ - @NonNull - public String getAutoIncrementingColumnName() { - throw new InvalidDBConfiguration( - String.format("This method may have been called in error. The model class %1s must contain " + - "an autoincrementing or single int/long primary key (if used in a ModelCache, this method may be called)", - getModelClass())); - } - - public boolean hasAutoIncrement(TModel model) { - Number id = getAutoIncrementingId(model); - return id != null && id.longValue() > 0; - } - - /** - * Called when we want to save our {@link ForeignKey} objects. usually during insert + update. - * This method is overridden when {@link ForeignKey} specified - */ - public void saveForeignKeys(@NonNull TModel model, @NonNull DatabaseWrapper wrapper) { - - } - - /** - * Called when we want to delete our {@link ForeignKey} objects. During deletion {@link #delete(Object, DatabaseWrapper)} - * This method is overridden when {@link ForeignKey} specified - */ - public void deleteForeignKeys(@NonNull TModel model, @NonNull DatabaseWrapper wrapper) { - - } - - /** - * @return A set of columns that represent the caching columns. - */ - @NonNull - public String[] createCachingColumns() { - return new String[]{getAutoIncrementingColumnName()}; - } - - /** - * @return The array of columns (in order) that represent what are cached. - */ - public String[] getCachingColumns() { - if (cachingColumns == null) { - cachingColumns = createCachingColumns(); - } - return cachingColumns; - } - - /** - * Loads all primary keys from the {@link FlowCursor} into the inValues. The size of the array must - * match all primary keys. This method gets generated when caching is enabled. - * - * @param inValues The reusable array of values to populate. - * @param cursor The cursor to load from. - * @return The populated set of values to load from cache. - */ - public Object[] getCachingColumnValuesFromCursor(@NonNull Object[] inValues, - @NonNull FlowCursor cursor) { - throwCachingError(); - return null; - } - - /** - * @param cursor The cursor to load caching id from. - * @return The single cache column from cursor (if single). - */ - public Object getCachingColumnValueFromCursor(@NonNull FlowCursor cursor) { - throwSingleCachingError(); - return null; - } - - /** - * Loads all primary keys from the {@link TModel} into the inValues. The size of the array must - * match all primary keys. This method gets generated when caching is enabled. It converts the primary fields - * of the {@link TModel} into the array of values the caching mechanism uses. - * - * @param inValues The reusable array of values to populate. - * @param TModel The model to load from. - * @return The populated set of values to load from cache. - */ - public Object[] getCachingColumnValuesFromModel(@NonNull Object[] inValues, @NonNull TModel TModel) { - throwCachingError(); - return null; - } - - /** - * @param model The model to load cache column data from. - * @return The single cache column from model (if single). - */ - public Object getCachingColumnValueFromModel(@NonNull TModel model) { - throwSingleCachingError(); - return null; - } - - public void storeModelInCache(@NonNull TModel model) { - getModelCache().addModel(getCachingId(model), model); - } - - public void removeModelFromCache(@NonNull TModel model) { - getModelCache().removeModel(getCachingId(model)); - } - - public ModelCache getModelCache() { - if (modelCache == null) { - modelCache = createModelCache(); - } - return modelCache; - } - - public Object getCachingId(@NonNull Object[] inValues) { - if (inValues.length == 1) { - // if it exists in cache no matter the query we will use that one - return inValues[0]; - } else { - return getCacheConverter().getCachingKey(inValues); - } - } - - public Object getCachingId(@NonNull TModel model) { - return getCachingId(getCachingColumnValuesFromModel(new Object[getCachingColumns().length], model)); - } - - public ModelSaver getModelSaver() { - if (modelSaver == null) { - modelSaver = createSingleModelSaver(); - modelSaver.setModelAdapter(this); - } - return modelSaver; - } - - public ListModelSaver getListModelSaver() { - if (listModelSaver == null) { - listModelSaver = createListModelSaver(); - } - return listModelSaver; - } - - protected ModelSaver createSingleModelSaver() { - return new ModelSaver<>(); - } - - protected ListModelSaver createListModelSaver() { - return new ListModelSaver<>(getModelSaver()); - } - - /** - * Sets how this {@link ModelAdapter} saves its objects. - * - * @param modelSaver The saver to use. - */ - public void setModelSaver(@NonNull ModelSaver modelSaver) { - this.modelSaver = modelSaver; - this.modelSaver.setModelAdapter(this); - } - - /** - * Reloads relationships when loading from {@link FlowCursor} in a model that's cacheable. By having - * relationships with cached models, the retrieval will be very fast. - * - * @param cursor The cursor to reload from. - */ - public void reloadRelationships(@NonNull TModel model, @NonNull FlowCursor cursor) { - if (!cachingEnabled()) { - throwCachingError(); - } - } - - @Override - public boolean cachingEnabled() { - return false; - } - - public int getCacheSize() { - return Table.DEFAULT_CACHE_SIZE; - } - - public IMultiKeyCacheConverter getCacheConverter() { - throw new InvalidDBConfiguration("For multiple primary keys, a public static IMultiKeyCacheConverter field must" + - "be marked with @MultiCacheField in the corresponding model class. The resulting key" + - "must be a unique combination of the multiple keys, otherwise inconsistencies may occur."); - } - - public ModelCache createModelCache() { - return new SimpleMapCache<>(getCacheSize()); - } - - /** - * @return The query used to create this table. - */ - public abstract String getCreationQuery(); - - /** - * Retrieves a property by name from the table via the corresponding generated "_Table" class. Useful - * when you want to dynamically get a property from an {@link ModelAdapter} and do an operation on it. - * - * @param columnName The column name of the property. - * @return The property from the corresponding Table class. - */ - public abstract Property getProperty(String columnName); - - /** - * @return An array of column properties, in order of declaration. - */ - public abstract IProperty[] getAllColumnProperties(); - - /** - * @return The query used to insert a model using a {@link SQLiteStatement} - */ - protected String getInsertStatementQuery() { - return getCompiledStatementQuery(); - } - - /** - * @return The normal query used in saving a model if we use a {@link SQLiteStatement}. - */ - protected abstract String getCompiledStatementQuery(); - - protected abstract String getUpdateStatementQuery(); - - protected abstract String getDeleteStatementQuery(); - - /** - * @return The conflict algorithm to use when updating a row in this table. - */ - public ConflictAction getUpdateOnConflictAction() { - return ConflictAction.ABORT; - } - - /** - * @return The conflict algorithm to use when inserting a row in this table. - */ - public ConflictAction getInsertOnConflictAction() { - return ConflictAction.ABORT; - } - - /** - * @return When false, this table gets generated and associated with database, however it will not immediately - * get created upon startup. This is useful for keeping around legacy tables for migrations. - */ - public boolean createWithDatabase() { - return true; - } - - private void throwCachingError() { - throw new InvalidDBConfiguration( - String.format("This method may have been called in error. The model class %1s must contain" + - "an auto-incrementing or at least one primary key (if used in a ModelCache, this method may be called)", - getModelClass())); - } - - private void throwSingleCachingError() { - throw new InvalidDBConfiguration( - String.format("This method may have been called in error. The model class %1s must contain" + - "an auto-incrementing or one primary key (if used in a ModelCache, this method may be called)", - getModelClass())); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelViewAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelViewAdapter.java deleted file mode 100644 index 337e4cd1a..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ModelViewAdapter.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; - -/** - * Description: The base class for a {@link TModelView} adapter that defines how it interacts with the DB. - */ -public abstract class ModelViewAdapter - extends InstanceAdapter { - - public ModelViewAdapter(@NonNull DatabaseDefinition databaseDefinition) { - super(databaseDefinition); - } - - /** - * @return a string of the query that is used to create this model view. - */ - public abstract String getCreationQuery(); - - /** - * @return The name of this view in the database - */ - public abstract String getViewName(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/NoModificationModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/NoModificationModel.java deleted file mode 100644 index fdb59a9d7..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/NoModificationModel.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: A convenience class for {@link ReadOnlyModel}. - */ -abstract class NoModificationModel implements ReadOnlyModel { - - private transient RetrievalAdapter adapter; - - @SuppressWarnings("unchecked") - public boolean exists() { - return getRetrievalAdapter().exists(this); - } - - @SuppressWarnings("unchecked") - public boolean exists(@NonNull DatabaseWrapper databaseWrapper) { - return getRetrievalAdapter().exists(this, databaseWrapper); - } - - @SuppressWarnings("unchecked") - public void load() { - getRetrievalAdapter().load(this); - } - - @SuppressWarnings("unchecked") - public void load(@NonNull DatabaseWrapper wrapper) { - getRetrievalAdapter().load(this, wrapper); - } - - public RetrievalAdapter getRetrievalAdapter() { - if (adapter == null) { - adapter = FlowManager.getInstanceAdapter(getClass()); - } - return adapter; - } - - /** - * Gets thrown when an operation is not valid for the SQL View - */ - static class InvalidSqlViewOperationException extends RuntimeException { - - InvalidSqlViewOperationException(String detailMessage) { - super(detailMessage); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/QueryModelAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/QueryModelAdapter.java deleted file mode 100644 index f00637fae..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/QueryModelAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.annotation.QueryModel; -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.sql.language.OperatorGroup; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: The baseclass for adapters to {@link QueryModel} that defines how it interacts with the DB. The - * where query is not defined here, rather its determined by the query used. - */ -public abstract class QueryModelAdapter extends - InstanceAdapter { - - public QueryModelAdapter(DatabaseDefinition databaseDefinition) { - super(databaseDefinition); - } - - @Override - public OperatorGroup getPrimaryConditionClause(@NonNull TQueryModel model) { - throw new UnsupportedOperationException("QueryModels cannot check for existence"); - } - - @Override - public boolean exists(@NonNull TQueryModel model) { - throw new UnsupportedOperationException("QueryModels cannot check for existence"); - } - - @Override - public boolean exists(@NonNull TQueryModel model, @NonNull DatabaseWrapper databaseWrapper) { - throw new UnsupportedOperationException("QueryModels cannot check for existence"); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ReadOnlyModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ReadOnlyModel.java deleted file mode 100644 index d0bb43f31..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/ReadOnlyModel.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.sql.migration.Migration; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -public interface ReadOnlyModel { - - /** - * Loads from the database the most recent version of the model based on it's primary keys. - */ - void load(); - - /** - * Loads from the database the most recent version of the model based on it's primary keys. - * - * @param wrapper Database object to use. Useful for {@link Migration} classes. - */ - void load(@NonNull DatabaseWrapper wrapper); - - /** - * @return true if this object exists in the DB. It combines all of it's primary key fields - * into a SELECT query and checks to see if any results occur. - */ - boolean exists(); - - /** - * @param wrapper Database object to use. Useful for {@link Migration} classes. - * @return true if this object exists in the DB. It combines all of it's primary key fields - * into a SELECT query and checks to see if any results occur. - */ - boolean exists(@NonNull DatabaseWrapper wrapper); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java deleted file mode 100644 index 83a853814..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/RetrievalAdapter.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.raizlabs.android.dbflow.structure; - -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseConfig; -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.config.TableConfig; -import com.raizlabs.android.dbflow.sql.language.OperatorGroup; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.sql.queriable.ListModelLoader; -import com.raizlabs.android.dbflow.sql.queriable.SingleModelLoader; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: Provides a base retrieval class for all {@link Model} backed - * adapters. - */ -@SuppressWarnings("NullableProblems") -public abstract class RetrievalAdapter { - - private SingleModelLoader singleModelLoader; - private ListModelLoader listModelLoader; - - private TableConfig tableConfig; - - public RetrievalAdapter(@NonNull DatabaseDefinition databaseDefinition) { - DatabaseConfig databaseConfig = FlowManager.getConfig() - .getConfigForDatabase(databaseDefinition.getAssociatedDatabaseClassFile()); - if (databaseConfig != null) { - tableConfig = databaseConfig.getTableConfigForTable(getModelClass()); - if (tableConfig != null) { - if (tableConfig.singleModelLoader() != null) { - singleModelLoader = tableConfig.singleModelLoader(); - } - - if (tableConfig.listModelLoader() != null) { - listModelLoader = tableConfig.listModelLoader(); - } - } - } - } - - /** - * Force loads the model from the DB. Even if caching is enabled it will requery the object. - */ - public void load(@NonNull TModel model) { - load(model, FlowManager.getDatabaseForTable(getModelClass()).getWritableDatabase()); - } - - /** - * Force loads the model from the DB. Even if caching is enabled it will requery the object. - */ - public void load(@NonNull TModel model, DatabaseWrapper databaseWrapper) { - getNonCacheableSingleModelLoader().load(databaseWrapper, - SQLite.select() - .from(getModelClass()) - .where(getPrimaryConditionClause(model)).getQuery(), - model); - } - - /** - * Assigns the {@link Cursor} data into the specified {@link TModel} - * - * @param model The model to assign cursor data to - * @param cursor The cursor to load into the model - */ - public abstract void loadFromCursor(@NonNull FlowCursor cursor, @NonNull TModel model); - - /** - * @param model The model to query values from - * @return True if it exists as a row in the corresponding database table - */ - public boolean exists(@NonNull TModel model) { - return exists(model, FlowManager.getDatabaseForTable(getModelClass()).getWritableDatabase()); - } - - /** - * @param model The model to query values from - * @return True if it exists as a row in the corresponding database table - */ - public abstract boolean exists(@NonNull TModel model, - @NonNull DatabaseWrapper databaseWrapper); - - /** - * @param model The primary condition clause. - * @return The clause that contains necessary primary conditions for this table. - */ - public abstract OperatorGroup getPrimaryConditionClause(@NonNull TModel model); - - /** - * @return the model class this adapter corresponds to - */ - @NonNull - public abstract Class getModelClass(); - - @Nullable - protected TableConfig getTableConfig() { - return tableConfig; - } - - /** - * @return A new {@link ListModelLoader}, caching will override this loader instance. - */ - @NonNull - public ListModelLoader getListModelLoader() { - if (listModelLoader == null) { - listModelLoader = createListModelLoader(); - } - return listModelLoader; - } - - /** - * @return A new {@link ListModelLoader}, caching will override this loader instance. - */ - @NonNull - protected ListModelLoader createListModelLoader() { - return new ListModelLoader<>(getModelClass()); - } - - /** - * @return A new {@link SingleModelLoader}, caching will override this loader instance. - */ - @NonNull - protected SingleModelLoader createSingleModelLoader() { - return new SingleModelLoader<>(getModelClass()); - } - - @NonNull - public SingleModelLoader getSingleModelLoader() { - if (singleModelLoader == null) { - singleModelLoader = createSingleModelLoader(); - } - return singleModelLoader; - } - - /** - * @return A new instance of a {@link SingleModelLoader}. Subsequent calls do not cache - * this object so it's recommended only calling this in bulk if possible. - */ - @NonNull - public SingleModelLoader getNonCacheableSingleModelLoader() { - return new SingleModelLoader<>(getModelClass()); - } - - /** - * @return A new instance of a {@link ListModelLoader}. Subsequent calls do not cache - * this object so it's recommended only calling this in bulk if possible. - */ - @NonNull - public ListModelLoader getNonCacheableListModelLoader() { - return new ListModelLoader<>(getModelClass()); - } - - /** - * Overrides the default implementation and allows you to provide your own implementation. Defines - * how a single {@link TModel} is loaded. - * - * @param singleModelLoader The loader to use. - */ - public void setSingleModelLoader(@NonNull SingleModelLoader singleModelLoader) { - this.singleModelLoader = singleModelLoader; - } - - /** - * Overrides the default implementation and allows you to provide your own implementation. Defines - * how a list of {@link TModel} are loaded. - * - * @param listModelLoader The loader to use. - */ - public void setListModelLoader(@NonNull ListModelLoader listModelLoader) { - this.listModelLoader = listModelLoader; - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/LruCache.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/LruCache.java deleted file mode 100644 index be531cd68..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/LruCache.java +++ /dev/null @@ -1,338 +0,0 @@ -package com.raizlabs.android.dbflow.structure.cache; - -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.util.LinkedHashMap; -import java.util.Locale; -import java.util.Map; - -/** - * Static library version of {@link android.util.LruCache}. Used to write apps that run on API levels prior to 12. When - * running on API level 12 or above, this implementation is still used; it does not try to switch to the framework's - * implementation. See the framework SDK documentation for a class overview. - * - * @see {@link android.util.LruCache} - * @deprecated - */ -@Deprecated -public class LruCache { - private final LinkedHashMap map; - - /** - * Size of this cache in units. Not necessarily the number of elements. - */ - private int size; - - private int maxSize; - - private int putCount; - - private int createCount; - - private int evictionCount; - - private int hitCount; - - private int missCount; - - /** - * @param maxSize for caches that do not override {@link #sizeOf}, this is the maximum number of entries in the - * cache. For all other caches, this is the maximum sum of the sizes of the entries in this cache. - */ - public LruCache(int maxSize) { - if (maxSize <= 0) { - throw new IllegalArgumentException("maxSize <= 0"); - } - this.maxSize = maxSize; - this.map = new LinkedHashMap<>(0, 0.75f, true); - } - - /** - * Sets the size of the cache. - * - * @param maxSize The new maximum size. - */ - public void resize(int maxSize) { - if (maxSize <= 0) { - throw new IllegalArgumentException("maxSize <= 0"); - } - - synchronized (this) { - this.maxSize = maxSize; - } - trimToSize(maxSize); - } - - /** - * Returns the value for {@code key} if it exists in the cache or can be created by {@code #create}. If a value was - * returned, it is moved to the head of the queue. This returns null if a value is not cached and cannot be - * created. - */ - public final V get(K key) { - if (key == null) { - throw new NullPointerException("key == null"); - } - - V mapValue; - synchronized (this) { - mapValue = map.get(key); - if (mapValue != null) { - hitCount++; - return mapValue; - } - missCount++; - } - - /* - * Attempt to create a value. This may take a long time, and the map - * may be different when create() returns. If a conflicting value was - * added to the map while create() was working, we leave that value in - * the map and release the created value. - */ - - V createdValue = create(key); - if (createdValue == null) { - return null; - } - - synchronized (this) { - createCount++; - mapValue = map.put(key, createdValue); - - if (mapValue != null) { - // There was a conflict so undo that last put - map.put(key, mapValue); - } else { - size += safeSizeOf(key, createdValue); - } - } - - if (mapValue != null) { - entryRemoved(false, key, createdValue, mapValue); - return mapValue; - } else { - trimToSize(maxSize); - return createdValue; - } - } - - /** - * Caches {@code value} for {@code key}. The value is moved to the head of the queue. - * - * @return the previous value mapped by {@code key}. - */ - public final V put(K key, V value) { - if (key == null || value == null) { - throw new NullPointerException("key == null || value == null"); - } - - V previous; - synchronized (this) { - putCount++; - size += safeSizeOf(key, value); - previous = map.put(key, value); - if (previous != null) { - size -= safeSizeOf(key, previous); - } - } - - if (previous != null) { - entryRemoved(false, key, previous, value); - } - - trimToSize(maxSize); - return previous; - } - - /** - * Remove the eldest entries until the total of remaining entries is at or below the requested size. - * - * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements. - */ - public void trimToSize(int maxSize) { - while (true) { - K key; - V value; - synchronized (this) { - if (size < 0 || (map.isEmpty() && size != 0)) { - throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); - } - - if (size <= maxSize || map.isEmpty()) { - break; - } - - Map.Entry toEvict = map.entrySet().iterator().next(); - key = toEvict.getKey(); - value = toEvict.getValue(); - map.remove(key); - size -= safeSizeOf(key, value); - evictionCount++; - } - - entryRemoved(true, key, value, null); - } - } - - /** - * Removes the entry for {@code key} if it exists. - * - * @return the previous value mapped by {@code key}. - */ - public final V remove(K key) { - if (key == null) { - throw new NullPointerException("key == null"); - } - - V previous; - synchronized (this) { - previous = map.remove(key); - if (previous != null) { - size -= safeSizeOf(key, previous); - } - } - - if (previous != null) { - entryRemoved(false, key, previous, null); - } - - return previous; - } - - /** - * Called for entries that have been evicted or removed. This method is invoked when a value is evicted to make - * space, removed by a call to {@link #remove}, or replaced by a call to {@link #put}. The default implementation - * does nothing. - *

The method is called without synchronization: other threads may access the cache while this method is - * executing. - * - * @param evicted true if the entry is being removed to make space, false if the removal was caused by a {@link - * #put} or {@link #remove}. - * @param newValue the new value for {@code key}, if it exists. If non-null, this removal was caused by a {@link - * #put}. Otherwise it was caused by an eviction or a {@link #remove}. - */ - protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) { - } - - /** - * Called after a cache miss to compute a value for the corresponding key. Returns the computed value or null if no - * value can be computed. The default implementation returns null. - *

The method is called without synchronization: other threads may access the cache while this method is - * executing. - *

If a value for {@code key} exists in the cache when this method returns, the created value will be released - * with {@link #entryRemoved} and discarded. This can occur when multiple threads request the same key at the same - * time (causing multiple values to be created), or when one thread calls {@link #put} while another is creating a - * value for the same key. - */ - protected V create(K key) { - return null; - } - - private int safeSizeOf(K key, V value) { - int result = sizeOf(key, value); - if (result < 0) { - throw new IllegalStateException("Negative size: " + key + "=" + value); - } - return result; - } - - /** - * Returns the size of the entry for {@code key} and {@code value} in user-defined units. The default - * implementation returns 1 so that size is the number of entries and max size is the maximum number of entries. - *

- *

An entry's size must not change while it is in the cache. - */ - protected int sizeOf(K key, V value) { - return 1; - } - - /** - * Clear the cache, calling {@link #entryRemoved} on each removed entry. - */ - public final void evictAll() { - trimToSize(-1); // -1 will evict 0-sized elements - } - - /** - * For caches that do not override {@link #sizeOf}, this returns the number of entries in the cache. For all other - * caches, this returns the sum of the sizes of the entries in this cache. - */ - public synchronized final int size() { - return size; - } - - /** - * For caches that do not override {@link #sizeOf}, this returns the maximum number of entries in the cache. For all - * other caches, this returns the maximum sum of the sizes of the entries in this cache. - */ - public synchronized final int maxSize() { - return maxSize; - } - - /** - * Returns the number of times {@link #get} returned a value that was already present in the cache. - */ - public synchronized final int hitCount() { - return hitCount; - } - - /** - * Returns the number of times {@link #get} returned null or required a new value to be created. - */ - public synchronized final int missCount() { - return missCount; - } - - /** - * Returns the number of times {@link #create(Object)} returned a value. - */ - public synchronized final int createCount() { - return createCount; - } - - /** - * Returns the number of times {@link #put} was called. - */ - public synchronized final int putCount() { - return putCount; - } - - /** - * Returns the number of values that have been evicted. - */ - public synchronized final int evictionCount() { - return evictionCount; - } - - /** - * Returns a copy of the current contents of the cache, ordered from least recently accessed to most recently - * accessed. - */ - public synchronized final Map snapshot() { - return new LinkedHashMap<>(map); - } - - @Override - public synchronized final String toString() { - int accesses = hitCount + missCount; - int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0; - return String.format(Locale.getDefault(), - "LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]", maxSize, - hitCount, missCount, hitPercent); - } -} - diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/ModelCache.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/ModelCache.java deleted file mode 100644 index 1bf321a05..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/ModelCache.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.raizlabs.android.dbflow.structure.cache; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Description: A generic cache for models that is implemented or can be implemented to your liking. - */ -public abstract class ModelCache { - - private CacheClass cache; - - /** - * Constructs new instance with a cache - * - * @param cache The arbitrary underlying cache class. - */ - public ModelCache(@NonNull CacheClass cache) { - this.cache = cache; - } - - /** - * Adds a model to this cache. - * - * @param id The id of the model to use. - * @param model The model to add - */ - public abstract void addModel(@Nullable Object id, @NonNull TModel model); - - /** - * Removes a model from this cache. - * - * @param id The id of the model to remove. - */ - public abstract TModel removeModel(@NonNull Object id); - - /** - * Clears out all models from this cache. - */ - public abstract void clear(); - - /** - * @param id The id of the model to retrieve. - * @return a model for the specified id. May be null. - */ - public abstract TModel get(@Nullable Object id); - - /** - * Sets a new size for the underlying cache (if applicable) and may destroy the cache. - * - * @param size The size of cache to set to - */ - public abstract void setCacheSize(int size); - - /** - * @return The cache that's backing this cache. - */ - public CacheClass getCache() { - return cache; - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/ModelLruCache.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/ModelLruCache.java deleted file mode 100644 index e7b72b431..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/ModelLruCache.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.raizlabs.android.dbflow.structure.cache; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.annotation.Table; - -/** - * Description: Provides an {@link com.raizlabs.android.dbflow.structure.cache.LruCache} under its hood - * and provides synchronization mechanisms. - */ -public class ModelLruCache extends ModelCache> { - - /** - * @param size The size, if less than or equal to 0 we set it to {@link Table#DEFAULT_CACHE_SIZE}. - */ - public static ModelLruCache newInstance(int size) { - if (size <= 0) { - size = Table.DEFAULT_CACHE_SIZE; - } - return new ModelLruCache<>(size); - } - - protected ModelLruCache(int size) { - super(new LruCache(size)); - } - - @Override - public void addModel(@Nullable Object id, @NonNull TModel model) { - if (id instanceof Number) { - synchronized (getCache()) { - Number number = ((Number) id); - getCache().put(number.longValue(), model); - } - } else { - throw new IllegalArgumentException("A ModelLruCache must use an id that can cast to" + - "a Number to convert it into a long"); - } - } - - @Override - public TModel removeModel(@NonNull Object id) { - TModel model; - if (id instanceof Number) { - synchronized (getCache()) { - model = getCache().remove(((Number) id).longValue()); - } - } else { - throw new IllegalArgumentException("A ModelLruCache uses an id that can cast to" + - "a Number to convert it into a long"); - } - return model; - } - - @Override - public void clear() { - synchronized (getCache()) { - getCache().evictAll(); - } - } - - @Override - public void setCacheSize(int size) { - getCache().resize(size); - } - - @Override - public TModel get(@Nullable Object id) { - if (id instanceof Number) { - return getCache().get(((Number) id).longValue()); - } else { - throw new IllegalArgumentException("A ModelLruCache must use an id that can cast to" + - "a Number to convert it into a long"); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/SimpleMapCache.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/SimpleMapCache.java deleted file mode 100644 index 59bbdeb5e..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/SimpleMapCache.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.raizlabs.android.dbflow.structure.cache; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.structure.Model; - -import java.util.HashMap; -import java.util.Map; - -/** - * Description: A simple implementation that keeps {@link Model} you interact with in memory. - */ -public class SimpleMapCache extends ModelCache> { - - /** - * Constructs new instance with a {@link HashMap} with the specified capacity. - * - * @param capacity The capacity to use on the hashmap. - */ - public SimpleMapCache(int capacity) { - super(new HashMap(capacity)); - } - - /** - * Constructs new instance with a cache - * - * @param cache The arbitrary underlying cache class. - */ - public SimpleMapCache(@NonNull Map cache) { - super(cache); - } - - @Override - public void addModel(@Nullable Object id, @NonNull TModel model) { - getCache().put(id, model); - } - - @Override - public TModel removeModel(@NonNull Object id) { - return getCache().remove(id); - } - - @Override - public void clear() { - getCache().clear(); - } - - @Override - public TModel get(@Nullable Object id) { - return getCache().get(id); - } - - @Override - public void setCacheSize(int size) { - FlowLog.log(FlowLog.Level.I, "The cache size for " + SimpleMapCache.class.getSimpleName() + - " is not re-configurable."); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/SparseArrayBasedCache.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/SparseArrayBasedCache.java deleted file mode 100644 index 4db00dca0..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/cache/SparseArrayBasedCache.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.raizlabs.android.dbflow.structure.cache; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.SparseArray; - -import com.raizlabs.android.dbflow.config.FlowLog; - -/** - * Description: A cache backed by a {@link android.util.SparseArray} - */ -public class SparseArrayBasedCache extends ModelCache> { - - /** - * Constructs new instance with a {@link android.util.SparseArray} cache - */ - public SparseArrayBasedCache() { - super(new SparseArray()); - } - - /** - * Constructs new instance with a {@link android.util.SparseArray} cache - * - * @param initialCapacity The initial capacity of the sparse array - */ - public SparseArrayBasedCache(int initialCapacity) { - super(new SparseArray(initialCapacity)); - } - - /** - * Constructs new instance with the specified {@link java.util.List} - * - * @param sparseArray The sparse array to use. - */ - public SparseArrayBasedCache(@NonNull SparseArray sparseArray) { - super(sparseArray); - } - - @Override - public void addModel(@Nullable Object id, @NonNull TModel model) { - if (id instanceof Number) { - synchronized (getCache()) { - getCache().put(((Number) id).intValue(), model); - } - } else { - throw new IllegalArgumentException("A SparseArrayBasedCache must use an id that can cast to " + - "a Number to convert it into a int"); - } - } - - @Override - public TModel removeModel(@NonNull Object id) { - TModel model = get(id); - synchronized (getCache()) { - getCache().remove(((Number) id).intValue()); - } - return model; - } - - @Override - public void clear() { - synchronized (getCache()) { - getCache().clear(); - } - } - - @Override - public void setCacheSize(int size) { - FlowLog.log(FlowLog.Level.I, "The cache size for " + SparseArrayBasedCache.class.getSimpleName() + " is not re-configurable."); - } - - @Override - public TModel get(@Nullable Object id) { - if (id instanceof Number) { - return getCache().get(((Number) id).intValue()); - } else { - throw new IllegalArgumentException("A SparseArrayBasedCache uses an id that can cast to " + - "a Number to convert it into a int"); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabase.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabase.java deleted file mode 100644 index bad1e661b..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabase.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.content.ContentValues; -import android.database.sqlite.SQLiteDatabase; -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Description: Specifies the android default implementation of a database. - */ -public class AndroidDatabase implements DatabaseWrapper { - - public static AndroidDatabase from(@NonNull SQLiteDatabase database) { - return new AndroidDatabase(database); - } - - private final SQLiteDatabase database; - - AndroidDatabase(@NonNull SQLiteDatabase database) { - this.database = database; - } - - @Override - public void execSQL(@NonNull String query) { - database.execSQL(query); - } - - @Override - public void beginTransaction() { - database.beginTransaction(); - } - - @Override - public void setTransactionSuccessful() { - database.setTransactionSuccessful(); - } - - @Override - public void endTransaction() { - database.endTransaction(); - } - - @Override - public int getVersion() { - return database.getVersion(); - } - - public SQLiteDatabase getDatabase() { - return database; - } - - @NonNull - @Override - public DatabaseStatement compileStatement(@NonNull String rawQuery) { - return AndroidDatabaseStatement.from(database.compileStatement(rawQuery), database); - } - - @NonNull - @Override - public FlowCursor rawQuery(@NonNull String query, @Nullable String[] selectionArgs) { - return FlowCursor.from(database.rawQuery(query, selectionArgs)); - } - - @Override - public long updateWithOnConflict(@NonNull String tableName, @NonNull ContentValues contentValues, @Nullable String where, @Nullable String[] whereArgs, int conflictAlgorithm) { - long count; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { - count = database.updateWithOnConflict(tableName, contentValues, where, whereArgs, conflictAlgorithm); - } else { - count = database.update(tableName, contentValues, where, whereArgs); - } - return count; - } - - @Override - public long insertWithOnConflict(@NonNull String tableName, @Nullable String nullColumnHack, @NonNull ContentValues values, int sqLiteDatabaseAlgorithmInt) { - long count; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { - count = database.insertWithOnConflict(tableName, nullColumnHack, values, sqLiteDatabaseAlgorithmInt); - } else { - count = database.insert(tableName, nullColumnHack, values); - } - return count; - } - - @NonNull - @Override - public FlowCursor query(@NonNull String tableName, - @Nullable String[] columns, - @Nullable String selection, - @Nullable String[] selectionArgs, - @Nullable String groupBy, - @Nullable String having, - @Nullable String orderBy) { - return FlowCursor.from(database.query(tableName, columns, selection, selectionArgs, groupBy, having, orderBy)); - } - - @Override - public int delete(@NonNull String tableName, @Nullable String whereClause, @Nullable String[] whereArgs) { - return database.delete(tableName, whereClause, whereArgs); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java deleted file mode 100644 index df44ce5c8..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/AndroidDatabaseStatement.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.database.Cursor; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteStatement; -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Description: - */ -public class AndroidDatabaseStatement extends BaseDatabaseStatement { - - public static AndroidDatabaseStatement from(@NonNull SQLiteStatement sqLiteStatement, - @NonNull SQLiteDatabase database) { - return new AndroidDatabaseStatement(sqLiteStatement, database); - } - - private final SQLiteStatement statement; - private final SQLiteDatabase database; - - AndroidDatabaseStatement(@NonNull SQLiteStatement statement, - @NonNull SQLiteDatabase database) { - this.statement = statement; - this.database = database; - } - - @NonNull - public SQLiteStatement getStatement() { - return statement; - } - - @Override - public long executeUpdateDelete() { - long count = 0; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - count = statement.executeUpdateDelete(); - } else { - statement.execute(); - - Cursor cursor = null; - try { - cursor = database.rawQuery("SELECT changes() AS affected_row_count", null); - if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) { - count = cursor.getLong(cursor.getColumnIndex("affected_row_count")); - } - } catch (SQLException e) { - // Handle exception here. - } finally { - if (cursor != null) { - cursor.close(); - } - } - } - return count; - } - - @Override - public void execute() { - statement.execute(); - } - - @Override - public void close() { - statement.close(); - } - - @Override - public long simpleQueryForLong() { - return statement.simpleQueryForLong(); - } - - @Nullable - @Override - public String simpleQueryForString() { - return statement.simpleQueryForString(); - } - - @Override - public long executeInsert() { - return statement.executeInsert(); - } - - @Override - public void bindString(int index, String s) { - statement.bindString(index, s); - } - - @Override - public void bindNull(int index) { - statement.bindNull(index); - } - - @Override - public void bindLong(int index, long aLong) { - statement.bindLong(index, aLong); - } - - @Override - public void bindDouble(int index, double aDouble) { - statement.bindDouble(index, aDouble); - } - - @Override - public void bindBlob(int index, byte[] bytes) { - statement.bindBlob(index, bytes); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseHelper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseHelper.java deleted file mode 100644 index 807056552..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseHelper.java +++ /dev/null @@ -1,233 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.database.sqlite.SQLiteException; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.config.NaturalOrderComparator; -import com.raizlabs.android.dbflow.sql.QueryBuilder; -import com.raizlabs.android.dbflow.sql.migration.Migration; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.ModelViewAdapter; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Description: - */ -public class BaseDatabaseHelper { - - /** - * Location where the migration files should exist. - */ - public static final String MIGRATION_PATH = "migrations"; - private final DatabaseDefinition databaseDefinition; - - public BaseDatabaseHelper(@NonNull DatabaseDefinition databaseDefinition) { - this.databaseDefinition = databaseDefinition; - } - - @NonNull - public DatabaseDefinition getDatabaseDefinition() { - return databaseDefinition; - } - - public void onCreate(@NonNull DatabaseWrapper db) { - checkForeignKeySupport(db); - executeTableCreations(db); - executeMigrations(db, -1, db.getVersion()); - executeViewCreations(db); - } - - public void onUpgrade(@NonNull DatabaseWrapper db, int oldVersion, int newVersion) { - checkForeignKeySupport(db); - executeTableCreations(db); - executeMigrations(db, oldVersion, newVersion); - executeViewCreations(db); - } - - public void onOpen(@NonNull DatabaseWrapper db) { - checkForeignKeySupport(db); - } - - public void onDowngrade(@NonNull DatabaseWrapper db, int oldVersion, int newVersion) { - checkForeignKeySupport(db); - } - - /** - * If foreign keys are supported, we turn it on the DB specified. - */ - protected void checkForeignKeySupport(@NonNull DatabaseWrapper database) { - if (databaseDefinition.isForeignKeysSupported()) { - database.execSQL("PRAGMA foreign_keys=ON;"); - FlowLog.log(FlowLog.Level.I, "Foreign Keys supported. Enabling foreign key features."); - } - } - - protected void executeTableCreations(@NonNull final DatabaseWrapper database){ - try { - database.beginTransaction(); - List modelAdapters = databaseDefinition.getModelAdapters(); - for (ModelAdapter modelAdapter : modelAdapters) { - if (modelAdapter.createWithDatabase()) { - try { - database.execSQL(modelAdapter.getCreationQuery()); - } catch (SQLiteException e) { - FlowLog.logError(e); - } - } - } - database.setTransactionSuccessful(); - } finally { - database.endTransaction(); - } - } - - /** - * This method executes CREATE TABLE statements as well as CREATE VIEW on the database passed. - */ - protected void executeViewCreations(@NonNull final DatabaseWrapper database){ - - try { - database.beginTransaction(); - List modelViews = databaseDefinition.getModelViewAdapters(); - for (ModelViewAdapter modelView : modelViews) { - QueryBuilder queryBuilder = new QueryBuilder() - .append("CREATE VIEW IF NOT EXISTS") - .appendSpaceSeparated(modelView.getViewName()) - .append("AS ") - .append(modelView.getCreationQuery()); - try { - database.execSQL(queryBuilder.getQuery()); - } catch (SQLiteException e) { - FlowLog.logError(e); - } - } - database.setTransactionSuccessful(); - } finally { - database.endTransaction(); - } - } - - protected void executeMigrations(@NonNull final DatabaseWrapper db, - final int oldVersion, final int newVersion) { - - // will try migrations file or execute migrations from code - try { - final List files = Arrays.asList(FlowManager.getContext().getAssets().list( - MIGRATION_PATH + "/" + databaseDefinition.getDatabaseName())); - Collections.sort(files, new NaturalOrderComparator()); - - final Map> migrationFileMap = new HashMap<>(); - for (String file : files) { - try { - final Integer version = Integer.valueOf(file.replace(".sql", "")); - List fileList = migrationFileMap.get(version); - if (fileList == null) { - fileList = new ArrayList<>(); - migrationFileMap.put(version, fileList); - } - fileList.add(file); - } catch (NumberFormatException e) { - FlowLog.log(FlowLog.Level.W, "Skipping invalidly named file: " + file, e); - } - } - - final Map> migrationMap = databaseDefinition.getMigrations(); - - final int curVersion = oldVersion + 1; - - try { - db.beginTransaction(); - - // execute migrations in order, migration file first before wrapped migration classes. - for (int i = curVersion; i <= newVersion; i++) { - List migrationFiles = migrationFileMap.get(i); - if (migrationFiles != null) { - for (String migrationFile : migrationFiles) { - executeSqlScript(db, migrationFile); - FlowLog.log(FlowLog.Level.I, migrationFile + " executed successfully."); - } - } - - List migrationsList = migrationMap.get(i); - if (migrationsList != null) { - for (Migration migration : migrationsList) { - // before migration - migration.onPreMigrate(); - - // migrate - migration.migrate(db); - - // after migration cleanup - migration.onPostMigrate(); - FlowLog.log(FlowLog.Level.I, migration.getClass() + " executed successfully."); - } - } - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } catch (IOException e) { - FlowLog.log(FlowLog.Level.E, "Failed to execute migrations.", e); - } - - } - - /** - * Supports multiline sql statements with ended with the standard ";" - * - * @param db The database to run it on - * @param file the file name in assets/migrations that we read from - */ - private void executeSqlScript(@NonNull DatabaseWrapper db, - @NonNull String file) { - try { - final InputStream input = FlowManager.getContext().getAssets().open(MIGRATION_PATH + "/" + getDatabaseDefinition().getDatabaseName() + "/" + file); - final BufferedReader reader = new BufferedReader(new InputStreamReader(input)); - String line; - - // ends line with SQL - String querySuffix = ";"; - - // standard java comments - String queryCommentPrefix = "--"; - StringBuffer query = new StringBuffer(); - - while ((line = reader.readLine()) != null) { - line = line.trim(); - boolean isEndOfQuery = line.endsWith(querySuffix); - if (line.startsWith(queryCommentPrefix)) { - continue; - } - if (isEndOfQuery) { - line = line.substring(0, line.length() - querySuffix.length()); - } - query.append(" ").append(line); - if (isEndOfQuery) { - db.execSQL(query.toString()); - query = new StringBuffer(); - } - } - - String queryString = query.toString(); - if (queryString.trim().length() > 0) { - db.execSQL(queryString); - } - } catch (IOException e) { - FlowLog.log(FlowLog.Level.E, "Failed to execute " + file, e); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java deleted file mode 100644 index 4918b70bb..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/BaseDatabaseStatement.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.support.annotation.Nullable; - -/** - * Description: Default implementation for some {@link DatabaseStatement} methods. - */ -public abstract class BaseDatabaseStatement implements DatabaseStatement { - - @Override - public void bindStringOrNull(int index, @Nullable String s) { - if (s != null) { - bindString(index, s); - } else { - bindNull(index); - } - } - - @Override - public void bindNumber(int index, @Nullable Number number) { - bindNumberOrNull(index, number); - } - - @Override - public void bindNumberOrNull(int index, @Nullable Number number) { - if (number != null) { - bindLong(index, number.longValue()); - } else { - bindNull(index); - } - } - - @Override - public void bindDoubleOrNull(int index, @Nullable Double aDouble) { - if (aDouble != null) { - bindDouble(index, aDouble); - } else { - bindNull(index); - } - } - - @Override - public void bindFloatOrNull(int index, @Nullable Float aFloat) { - if (aFloat != null) { - bindDouble(index, aFloat); - } else { - bindNull(index); - } - } - - @Override - public void bindBlobOrNull(int index, @Nullable byte[] bytes) { - if (bytes != null) { - bindBlob(index, bytes); - } else { - bindNull(index); - } - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperDelegate.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperDelegate.java deleted file mode 100644 index edfb62be7..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperDelegate.java +++ /dev/null @@ -1,316 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.content.Context; -import android.database.sqlite.SQLiteOpenHelper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.structure.database.transaction.DefaultTransactionQueue; -import com.raizlabs.android.dbflow.structure.database.transaction.ITransaction; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Description: An abstraction from some parts of the {@link SQLiteOpenHelper} where this can be - * used in other helper class definitions. - */ -public class DatabaseHelperDelegate extends BaseDatabaseHelper { - - public static final String TEMP_DB_NAME = "temp-"; - - public static String getTempDbFileName(DatabaseDefinition databaseDefinition) { - return TEMP_DB_NAME + databaseDefinition.getDatabaseName() + ".db"; - } - - private DatabaseHelperListener databaseHelperListener; - - @Nullable private final OpenHelper backupHelper; - - public DatabaseHelperDelegate(DatabaseHelperListener databaseHelperListener, - DatabaseDefinition databaseDefinition, @Nullable OpenHelper backupHelper) { - super(databaseDefinition); - this.databaseHelperListener = databaseHelperListener; - this.backupHelper = backupHelper; - } - - public void performRestoreFromBackup() { - movePrepackagedDB(getDatabaseDefinition().getDatabaseFileName(), - getDatabaseDefinition().getDatabaseFileName()); - - if (getDatabaseDefinition().backupEnabled()) { - if (backupHelper == null) { - throw new IllegalStateException("the passed backup helper was null, even though backup is enabled. " + - "Ensure that its passed in."); - } - restoreDatabase(getTempDbFileName(), getDatabaseDefinition().getDatabaseFileName()); - backupHelper.getDatabase(); - } - } - - /** - * @param databaseHelperListener Listens for operations the DB and allow you to provide extra - * functionality. - */ - public void setDatabaseHelperListener(DatabaseHelperListener databaseHelperListener) { - this.databaseHelperListener = databaseHelperListener; - } - - @Override - public void onCreate(@NonNull DatabaseWrapper db) { - if (databaseHelperListener != null) { - databaseHelperListener.onCreate(db); - } - super.onCreate(db); - } - - @Override - public void onUpgrade(@NonNull DatabaseWrapper db, int oldVersion, int newVersion) { - if (databaseHelperListener != null) { - databaseHelperListener.onUpgrade(db, oldVersion, newVersion); - } - super.onUpgrade(db, oldVersion, newVersion); - } - - @Override - public void onOpen(@NonNull DatabaseWrapper db) { - if (databaseHelperListener != null) { - databaseHelperListener.onOpen(db); - } - super.onOpen(db); - } - - @Override - public void onDowngrade(@NonNull DatabaseWrapper db, int oldVersion, int newVersion) { - if (databaseHelperListener != null) { - databaseHelperListener.onDowngrade(db, oldVersion, newVersion); - } - super.onDowngrade(db, oldVersion, newVersion); - } - - /** - * @return the temporary database file name for when we have backups enabled - * {@link DatabaseDefinition#backupEnabled()} - */ - private String getTempDbFileName() { - return getTempDbFileName(getDatabaseDefinition()); - } - - /** - * Copies over the prepackaged DB into the main DB then deletes the existing DB to save storage space. If - * we have a backup that exists - * - * @param databaseName The name of the database to copy over - * @param prepackagedName The name of the prepackaged db file - */ - public void movePrepackagedDB(String databaseName, String prepackagedName) { - final File dbPath = FlowManager.getContext().getDatabasePath(databaseName); - - // If the database already exists, and is ok return - if (dbPath.exists() && (!getDatabaseDefinition().areConsistencyChecksEnabled() || - (getDatabaseDefinition().areConsistencyChecksEnabled() - && isDatabaseIntegrityOk(getWritableDatabase())))) { - return; - } - - // Make sure we have a path to the file - dbPath.getParentFile().mkdirs(); - - // Try to copy database file - try { - // check existing and use that as backup - File existingDb = FlowManager.getContext().getDatabasePath(getTempDbFileName()); - InputStream inputStream; - // if it exists and the integrity is ok we use backup as the main DB is no longer valid - if (existingDb.exists() && (!getDatabaseDefinition().backupEnabled() || getDatabaseDefinition().backupEnabled() - && backupHelper != null && isDatabaseIntegrityOk(backupHelper.getDatabase()))) { - inputStream = new FileInputStream(existingDb); - } else { - inputStream = FlowManager.getContext().getAssets().open(prepackagedName); - } - writeDB(dbPath, inputStream); - - } catch (IOException e) { - FlowLog.log(FlowLog.Level.W, "Failed to open file", e); - } - } - - /** - * Pulled partially from code, it runs a "PRAGMA quick_check(1)" to see if the database is ok. - * This method will {@link #restoreBackUp()} if they are enabled on the database if this check fails. So - * use with caution and ensure that you backup the database often! - * - * @return true if the database is ok, false if the consistency has been compromised. - */ - public boolean isDatabaseIntegrityOk() { - return isDatabaseIntegrityOk(getWritableDatabase()); - } - - /** - * Pulled partially from code, it runs a "PRAGMA quick_check(1)" to see if the database is ok. - * This method will {@link #restoreBackUp()} if they are enabled on the database if this check fails. So - * use with caution and ensure that you backup the database often! - * - * @return true if the database is ok, false if the consistency has been compromised. - */ - public boolean isDatabaseIntegrityOk(DatabaseWrapper databaseWrapper) { - boolean integrityOk = true; - - DatabaseStatement statement = null; - try { - statement = databaseWrapper.compileStatement("PRAGMA quick_check(1)"); - String result = statement.simpleQueryForString(); - if (!result.equalsIgnoreCase("ok")) { - // integrity_checker failed on main or attached databases - FlowLog.log(FlowLog.Level.E, "PRAGMA integrity_check on " + - getDatabaseDefinition().getDatabaseName() + " returned: " + result); - - integrityOk = false; - - if (getDatabaseDefinition().backupEnabled()) { - integrityOk = restoreBackUp(); - } - } - } finally { - if (statement != null) { - statement.close(); - } - } - return integrityOk; - } - - /** - * If integrity check fails, this method will use the backup db to fix itself. In order to prevent - * loss of data, please backup often! - */ - public boolean restoreBackUp() { - boolean success = true; - - File db = FlowManager.getContext().getDatabasePath(TEMP_DB_NAME + getDatabaseDefinition().getDatabaseName()); - File corrupt = FlowManager.getContext().getDatabasePath(getDatabaseDefinition().getDatabaseName()); - if (corrupt.delete()) { - try { - writeDB(corrupt, new FileInputStream(db)); - } catch (IOException e) { - FlowLog.logError(e); - success = false; - } - } else { - FlowLog.log(FlowLog.Level.E, "Failed to delete DB"); - } - return success; - } - - /** - * Writes the {@link InputStream} of the existing db to the file specified. - * - * @param dbPath The file to write to. - * @param existingDB The existing database file's input stream¬ - * @throws IOException - */ - private void writeDB(File dbPath, InputStream existingDB) throws IOException { - final OutputStream output = new FileOutputStream(dbPath); - - byte[] buffer = new byte[1024]; - int length; - - while ((length = existingDB.read(buffer)) > 0) { - output.write(buffer, 0, length); - } - - output.flush(); - output.close(); - existingDB.close(); - } - - /** - * Will use the already existing app database if {@link DatabaseDefinition#backupEnabled()} is true. If the existing - * is not there we will try to use the prepackaged database for that purpose. - * - * @param databaseName The name of the database to restore - * @param prepackagedName The name of the prepackaged db file - */ - public void restoreDatabase(String databaseName, String prepackagedName) { - final File dbPath = FlowManager.getContext().getDatabasePath(databaseName); - - // If the database already exists, return - if (dbPath.exists()) { - return; - } - - // Make sure we have a path to the file - dbPath.getParentFile().mkdirs(); - - // Try to copy database file - try { - // check existing and use that as backup - File existingDb = FlowManager.getContext().getDatabasePath(getDatabaseDefinition().getDatabaseFileName()); - InputStream inputStream; - // if it exists and the integrity is ok - if (existingDb.exists() && (getDatabaseDefinition().backupEnabled() - && backupHelper != null && isDatabaseIntegrityOk(backupHelper.getDatabase()))) { - inputStream = new FileInputStream(existingDb); - } else { - inputStream = FlowManager.getContext().getAssets().open(prepackagedName); - } - writeDB(dbPath, inputStream); - } catch (IOException e) { - FlowLog.logError(e); - } - } - - - /** - * Saves the database as a backup on the {@link DefaultTransactionQueue}. - * This will create a THIRD database to use as a backup to the backup in case somehow the overwrite fails. - */ - public void backupDB() { - if (!getDatabaseDefinition().backupEnabled() || !getDatabaseDefinition().areConsistencyChecksEnabled()) { - throw new IllegalStateException("Backups are not enabled for : " + getDatabaseDefinition().getDatabaseName() + ". Please consider adding " + - "both backupEnabled and consistency checks enabled to the Database annotation"); - } - - getDatabaseDefinition().beginTransactionAsync(new ITransaction() { - @SuppressWarnings("ResultOfMethodCallIgnored") - @Override - public void execute(DatabaseWrapper databaseWrapper) { - Context context = FlowManager.getContext(); - File backup = context.getDatabasePath(getTempDbFileName()); - File temp = context.getDatabasePath(TEMP_DB_NAME + "-2-" + getDatabaseDefinition().getDatabaseFileName()); - - // if exists we want to delete it before rename - if (temp.exists()) { - temp.delete(); - } - - backup.renameTo(temp); - if (backup.exists()) { - backup.delete(); - } - File existing = context.getDatabasePath(getDatabaseDefinition().getDatabaseFileName()); - - try { - backup.getParentFile().mkdirs(); - writeDB(backup, new FileInputStream(existing)); - - temp.delete(); - } catch (Exception e) { - FlowLog.logError(e); - - } - } - }).build().execute(); - - } - - public DatabaseWrapper getWritableDatabase() { - return getDatabaseDefinition().getWritableDatabase(); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java deleted file mode 100644 index 99c1c9d06..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatement.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.database.sqlite.SQLiteStatement; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Description: Abstracts out a {@link SQLiteStatement}. - */ -public interface DatabaseStatement { - - long executeUpdateDelete(); - - void execute(); - - void close(); - - long simpleQueryForLong(); - - @Nullable - String simpleQueryForString(); - - long executeInsert(); - - void bindString(int index, String s); - - void bindStringOrNull(int index, @Nullable String s); - - void bindNull(int index); - - void bindLong(int index, long aLong); - - void bindNumber(int index, @Nullable Number number); - - void bindNumberOrNull(int index, @Nullable Number number); - - void bindDouble(int index, double aDouble); - - void bindDoubleOrNull(int index, @Nullable Double aDouble); - - void bindFloatOrNull(int index, @Nullable Float aFloat); - - void bindBlob(int index, byte[] bytes); - - void bindBlobOrNull(int index, @Nullable byte[] bytes); - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java deleted file mode 100644 index 1eac49388..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseStatementWrapper.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.runtime.NotifyDistributor; -import com.raizlabs.android.dbflow.sql.language.BaseQueriable; - -/** - * Description: Delegates all of its calls to the contained {@link DatabaseStatement}, while - * providing notification methods for when operations occur. - */ -public class DatabaseStatementWrapper extends BaseDatabaseStatement { - - @NonNull - private final DatabaseStatement databaseStatement; - private final BaseQueriable modelQueriable; - - public DatabaseStatementWrapper(@NonNull DatabaseStatement databaseStatement, - @NonNull BaseQueriable modelQueriable) { - this.databaseStatement = databaseStatement; - this.modelQueriable = modelQueriable; - } - - @Override - public long executeUpdateDelete() { - long affected = databaseStatement.executeUpdateDelete(); - if (affected > 0) { - NotifyDistributor.get().notifyTableChanged(modelQueriable.getTable(), - modelQueriable.getPrimaryAction()); - } - return affected; - } - - @Override - public void execute() { - databaseStatement.execute(); - } - - @Override - public void close() { - databaseStatement.close(); - } - - @Override - public long simpleQueryForLong() { - return databaseStatement.simpleQueryForLong(); - } - - @Nullable - @Override - public String simpleQueryForString() { - return databaseStatement.simpleQueryForString(); - } - - @Override - public long executeInsert() { - long affected = databaseStatement.executeInsert(); - if (affected > 0) { - NotifyDistributor.get().notifyTableChanged(modelQueriable.getTable(), - modelQueriable.getPrimaryAction()); - } - return affected; - } - - @Override - public void bindString(int index, String s) { - databaseStatement.bindString(index, s); - } - - @Override - public void bindNull(int index) { - databaseStatement.bindNull(index); - } - - @Override - public void bindLong(int index, long aLong) { - databaseStatement.bindLong(index, aLong); - } - - @Override - public void bindDouble(int index, double aDouble) { - databaseStatement.bindDouble(index, aDouble); - } - - @Override - public void bindBlob(int index, byte[] bytes) { - databaseStatement.bindBlob(index, bytes); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseWrapper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseWrapper.java deleted file mode 100644 index 8e7d8ffb2..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseWrapper.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.content.ContentValues; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Description: Provides a base implementation that wraps a database, so other database engines potentially can - * be used. - */ -public interface DatabaseWrapper { - - void execSQL(@NonNull String query); - - void beginTransaction(); - - void setTransactionSuccessful(); - - void endTransaction(); - - int getVersion(); - - @NonNull - DatabaseStatement compileStatement(@NonNull String rawQuery); - - @NonNull - FlowCursor rawQuery(@NonNull String query, @Nullable String[] selectionArgs); - - long updateWithOnConflict(@NonNull String tableName, - @NonNull ContentValues contentValues, - @Nullable String where, - @Nullable String[] whereArgs, int conflictAlgorithm); - - long insertWithOnConflict(@NonNull String tableName, - @Nullable String nullColumnHack, - @NonNull ContentValues values, - int sqLiteDatabaseAlgorithmInt); - - @NonNull - FlowCursor query(@NonNull String tableName, @Nullable String[] columns, @Nullable String selection, - @Nullable String[] selectionArgs, @Nullable String groupBy, - @Nullable String having, @Nullable String orderBy); - - int delete(@NonNull String tableName, @Nullable String whereClause, @Nullable String[] whereArgs); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java deleted file mode 100644 index 2f30379fd..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowCursor.java +++ /dev/null @@ -1,293 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.database.Cursor; -import android.database.CursorWrapper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Common {@link Cursor} class that wraps cursors we use in this library with convenience loading methods. - * This is used to help cut down on generated code size and potentially decrease method count. - */ -public class FlowCursor extends CursorWrapper { - - public static FlowCursor from(@NonNull Cursor cursor) { - if (cursor instanceof FlowCursor) { - return (FlowCursor) cursor; - } else { - return new FlowCursor(cursor); - } - } - - private Cursor cursor; // compatibility reasons - - private FlowCursor(@NonNull Cursor cursor) { - super(cursor); - this.cursor = cursor; - } - - // compatibility - public Cursor getWrappedCursor() { - return cursor; - } - - public String getStringOrDefault(int index, String defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getString(index); - } else { - return defValue; - } - } - - @Nullable - public String getStringOrDefault(String columnName) { - return getStringOrDefault(cursor.getColumnIndex(columnName)); - } - - @Nullable - public String getStringOrDefault(int index) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getString(index); - } else { - return null; - } - } - - public String getStringOrDefault(String columnName, String defValue) { - return getStringOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public int getIntOrDefault(String columnName) { - return getIntOrDefault(cursor.getColumnIndex(columnName)); - } - - public int getIntOrDefault(int index) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getInt(index); - } else { - return 0; - } - } - - public int getIntOrDefault(int index, int defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getInt(index); - } else { - return defValue; - } - } - - public int getIntOrDefault(String columnName, int defValue) { - return getIntOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public Integer getIntOrDefault(int index, Integer defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getInt(index); - } else { - return defValue; - } - } - - public Integer getIntOrDefault(String columnName, Integer defValue) { - return getIntOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public double getDoubleOrDefault(int index, double defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getDouble(index); - } else { - return defValue; - } - } - - public double getDoubleOrDefault(String columnName) { - return getDoubleOrDefault(cursor.getColumnIndex(columnName)); - } - - public double getDoubleOrDefault(int index) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getDouble(index); - } else { - return 0; - } - } - - public double getDoubleOrDefault(String columnName, double defValue) { - return getDoubleOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public Double getDoubleOrDefault(int index, Double defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getDouble(index); - } else { - return defValue; - } - } - - public Double getDoubleOrDefault(String columnName, Double defValue) { - return getDoubleOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public float getFloatOrDefault(int index, float defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getFloat(index); - } else { - return defValue; - } - } - - public float getFloatOrDefault(String columnName) { - return getFloatOrDefault(cursor.getColumnIndex(columnName)); - } - - public float getFloatOrDefault(int index) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getFloat(index); - } else { - return 0; - } - } - - public float getFloatOrDefault(String columnName, float defValue) { - return getFloatOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public Float getFloatOrDefault(int index, Float defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getFloat(index); - } else { - return defValue; - } - } - - public Float getFloatOrDefault(String columnName, Float defValue) { - return getFloatOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public long getLongOrDefault(int index, long defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getLong(index); - } else { - return defValue; - } - } - - public long getLongOrDefault(String columnName) { - return getLongOrDefault(cursor.getColumnIndex(columnName)); - } - - public long getLongOrDefault(int index) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getLong(index); - } else { - return 0; - } - } - - public long getLongOrDefault(String columnName, long defValue) { - return getLongOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public Long getLongOrDefault(int index, Long defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getLong(index); - } else { - return defValue; - } - } - - public Long getLongOrDefault(String columnName, Long defValue) { - return getLongOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public short getShortOrDefault(int index, short defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getShort(index); - } else { - return defValue; - } - } - - public short getShortOrDefault(String columnName) { - return getShortOrDefault(cursor.getColumnIndex(columnName)); - } - - public short getShortOrDefault(int index) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getShort(index); - } else { - return 0; - } - } - - public short getShortOrDefault(String columnName, short defValue) { - return getShortOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public Short getShortOrDefault(int index, Short defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getShort(index); - } else { - return defValue; - } - } - - public Short getShortOrDefault(String columnName, Short defValue) { - return getShortOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public byte[] getBlobOrDefault(String columnName) { - return getBlobOrDefault(cursor.getColumnIndex(columnName)); - } - - public byte[] getBlobOrDefault(int index) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getBlob(index); - } else { - return null; - } - } - - public byte[] getBlobOrDefault(int index, byte[] defValue) { - if (index != -1 && !cursor.isNull(index)) { - return cursor.getBlob(index); - } else { - return defValue; - } - } - - public byte[] getBlobOrDefault(String columnName, byte[] defValue) { - return getBlobOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public boolean getBooleanOrDefault(int index, boolean defValue) { - if (index != -1 && !cursor.isNull(index)) { - return getBoolean(index); - } else { - return defValue; - } - } - - public boolean getBooleanOrDefault(String columnName) { - return getBooleanOrDefault(cursor.getColumnIndex(columnName)); - } - - public boolean getBooleanOrDefault(int index) { - if (index != -1 && !cursor.isNull(index)) { - return getBoolean(index); - } else { - return false; - } - } - - public boolean getBooleanOrDefault(String columnName, boolean defValue) { - return getBooleanOrDefault(cursor.getColumnIndex(columnName), defValue); - } - - public boolean getBoolean(int index) { - return cursor.getInt(index) == 1; - } - -} - diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowSQLiteOpenHelper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowSQLiteOpenHelper.java deleted file mode 100644 index 922fff94c..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/FlowSQLiteOpenHelper.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowManager; - -/** - * Description: Wraps around the {@link SQLiteOpenHelper} and provides extra features for use in this library. - */ -public class FlowSQLiteOpenHelper extends SQLiteOpenHelper implements OpenHelper { - - private DatabaseHelperDelegate databaseHelperDelegate; - private AndroidDatabase androidDatabase; - - - public FlowSQLiteOpenHelper(@NonNull DatabaseDefinition databaseDefinition, - @NonNull DatabaseHelperListener listener) { - super(FlowManager.getContext(), databaseDefinition.isInMemory() ? null : databaseDefinition.getDatabaseFileName(), - null, databaseDefinition.getDatabaseVersion()); - - OpenHelper backupHelper = null; - if (databaseDefinition.backupEnabled()) { - // Temp database mirrors existing - backupHelper = new BackupHelper(FlowManager.getContext(), - DatabaseHelperDelegate.getTempDbFileName(databaseDefinition), - databaseDefinition.getDatabaseVersion(), databaseDefinition); - } - - databaseHelperDelegate = new DatabaseHelperDelegate(listener, databaseDefinition, backupHelper); - } - - @Override - public void performRestoreFromBackup() { - databaseHelperDelegate.performRestoreFromBackup(); - } - - @Nullable - @Override - public DatabaseHelperDelegate getDelegate() { - return databaseHelperDelegate; - } - - @Override - public boolean isDatabaseIntegrityOk() { - return databaseHelperDelegate.isDatabaseIntegrityOk(); - } - - @Override - public void backupDB() { - databaseHelperDelegate.backupDB(); - } - - @NonNull - @Override - public DatabaseWrapper getDatabase() { - if (androidDatabase == null || !androidDatabase.getDatabase().isOpen()) { - androidDatabase = AndroidDatabase.from(getWritableDatabase()); - } - return androidDatabase; - } - - /** - * Set a listener to listen for specific DB events and perform an action before we execute this classes - * specific methods. - * - * @param listener - */ - public void setDatabaseListener(@Nullable DatabaseHelperListener listener) { - databaseHelperDelegate.setDatabaseHelperListener(listener); - } - - @Override - public void onCreate(@NonNull SQLiteDatabase db) { - databaseHelperDelegate.onCreate(AndroidDatabase.from(db)); - } - - @Override - public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) { - databaseHelperDelegate.onUpgrade(AndroidDatabase.from(db), oldVersion, newVersion); - } - - @Override - public void onOpen(@NonNull SQLiteDatabase db) { - databaseHelperDelegate.onOpen(AndroidDatabase.from(db)); - } - - @Override - public void onDowngrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) { - databaseHelperDelegate.onDowngrade(AndroidDatabase.from(db), oldVersion, newVersion); - } - - @Override - public void closeDB() { - getDatabase(); - androidDatabase.getDatabase().close(); - } - - /** - * Simple helper to manage backup. - */ - private class BackupHelper extends SQLiteOpenHelper implements OpenHelper { - - private AndroidDatabase androidDatabase; - private final BaseDatabaseHelper baseDatabaseHelper; - - public BackupHelper(Context context, String name, int version, DatabaseDefinition databaseDefinition) { - super(context, name, null, version); - this.baseDatabaseHelper = new BaseDatabaseHelper(databaseDefinition); - } - - @NonNull - @Override - public DatabaseWrapper getDatabase() { - if (androidDatabase == null) { - androidDatabase = AndroidDatabase.from(getWritableDatabase()); - } - return androidDatabase; - } - - @Override - public void performRestoreFromBackup() { - } - - @Nullable - @Override - public DatabaseHelperDelegate getDelegate() { - return null; - } - - @Override - public boolean isDatabaseIntegrityOk() { - return false; - } - - @Override - public void backupDB() { - } - - @Override - public void setDatabaseListener(@Nullable DatabaseHelperListener helperListener) { - } - - @Override - public void onCreate(SQLiteDatabase db) { - baseDatabaseHelper.onCreate(AndroidDatabase.from(db)); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - baseDatabaseHelper.onUpgrade(AndroidDatabase.from(db), oldVersion, newVersion); - } - - @Override - public void onOpen(SQLiteDatabase db) { - baseDatabaseHelper.onOpen(AndroidDatabase.from(db)); - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - baseDatabaseHelper.onDowngrade(AndroidDatabase.from(db), oldVersion, newVersion); - } - - @Override - public void closeDB() { - } - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/OpenHelper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/OpenHelper.java deleted file mode 100644 index 280e3b80a..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/OpenHelper.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * Description: Abstracts out the {@link DatabaseHelperDelegate} into the one used in this library. - */ -public interface OpenHelper { - - void performRestoreFromBackup(); - - @NonNull - DatabaseWrapper getDatabase(); - - @Nullable - DatabaseHelperDelegate getDelegate(); - - boolean isDatabaseIntegrityOk(); - - void backupDB(); - - void setDatabaseListener(@Nullable DatabaseHelperListener helperListener); - - void closeDB(); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/DefaultTransactionManager.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/DefaultTransactionManager.java deleted file mode 100644 index 4f8d81ced..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/DefaultTransactionManager.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.runtime.BaseTransactionManager; - -/** - * Description: This class manages batch database interactions. It is useful for retrieving, updating, saving, - * and deleting lists of items. The bulk of DB operations should exist in this class. - */ -public class DefaultTransactionManager extends BaseTransactionManager { - - public DefaultTransactionManager(@NonNull DatabaseDefinition databaseDefinition) { - super(new DefaultTransactionQueue("DBFlow Transaction Queue"), databaseDefinition); - } - - public DefaultTransactionManager(@NonNull ITransactionQueue transactionQueue, - @NonNull DatabaseDefinition databaseDefinition) { - super(transactionQueue, databaseDefinition); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/DefaultTransactionQueue.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/DefaultTransactionQueue.java deleted file mode 100644 index 75503e926..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/DefaultTransactionQueue.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.os.Looper; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowLog; - -import java.util.Iterator; -import java.util.concurrent.LinkedBlockingQueue; - -/** - * Description: Handles concurrent requests to the database and puts them in FIFO order based on a - * {@link LinkedBlockingQueue}. As requests come in, they're placed in order and ran one at a time - * until the queue becomes empty. - */ -public class DefaultTransactionQueue extends Thread implements ITransactionQueue { - - private final LinkedBlockingQueue queue; - - private boolean isQuitting = false; - - /** - * Creates a queue with the specified name to ID it. - * - * @param name - */ - public DefaultTransactionQueue(String name) { - super(name); - queue = new LinkedBlockingQueue<>(); - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - Looper.prepare(); - android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); - Transaction transaction; - while (true) { - try { - transaction = queue.take(); - } catch (InterruptedException e) { - synchronized (this) { - if (isQuitting) { - synchronized (queue) { - queue.clear(); - } - return; - } - } - continue; - } - - if (!isQuitting) { - transaction.executeSync(); - } - } - } - - @Override - public void add(@NonNull Transaction runnable) { - synchronized (queue) { - if (!queue.contains(runnable)) { - queue.add(runnable); - } - } - } - - /** - * Cancels the specified request. - * - * @param runnable - */ - @Override - public void cancel(@NonNull Transaction runnable) { - synchronized (queue) { - if (queue.contains(runnable)) { - queue.remove(runnable); - } - } - } - - /** - * Cancels all requests by a specific tag - * - * @param tag - */ - @Override - public void cancel(@NonNull String tag) { - synchronized (queue) { - Iterator it = queue.iterator(); - while (it.hasNext()) { - Transaction next = it.next(); - if (next.name() != null && next.name().equals(tag)) { - it.remove(); - } - } - } - } - - @Override - public void startIfNotAlive() { - synchronized (this) { - if (!isAlive()) { - try { - start(); - } catch (IllegalThreadStateException i) { - // log if failure from thread is still alive. - FlowLog.log(FlowLog.Level.E, i); - } - } - } - } - - /** - * Quits this process - */ - @Override - public void quit() { - synchronized (this) { - isQuitting = true; - } - interrupt(); - } -} - diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/FastStoreModelTransaction.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/FastStoreModelTransaction.java deleted file mode 100644 index da5ba517c..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/FastStoreModelTransaction.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.InternalAdapter; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * Description: Similiar to {@link ProcessModelTransaction} in that it allows you to store a {@link List} of - * {@link Model}, except that it performs it as efficiently as possible. Also due to way the class operates, - * only one kind of {@link TModel} is allowed. - */ -public class FastStoreModelTransaction implements ITransaction { - - @NonNull - public static Builder saveBuilder(@NonNull InternalAdapter internalAdapter) { - return new Builder<>(new ProcessModelList() { - @Override - public void processModel(@NonNull List tModels, InternalAdapter adapter, DatabaseWrapper wrapper) { - adapter.saveAll(tModels, wrapper); - } - }, internalAdapter); - } - - @NonNull - public static Builder insertBuilder(@NonNull InternalAdapter internalAdapter) { - return new Builder<>(new ProcessModelList() { - @Override - public void processModel(@NonNull List tModels, InternalAdapter adapter, DatabaseWrapper wrapper) { - adapter.insertAll(tModels, wrapper); - } - }, internalAdapter); - } - - @NonNull - public static Builder updateBuilder(@NonNull InternalAdapter internalAdapter) { - return new Builder<>(new ProcessModelList() { - @Override - public void processModel(@NonNull List tModels, InternalAdapter adapter, DatabaseWrapper wrapper) { - adapter.updateAll(tModels, wrapper); - } - }, internalAdapter); - } - - @NonNull - public static Builder deleteBuilder(@NonNull InternalAdapter internalAdapter) { - return new Builder<>(new ProcessModelList() { - @Override - public void processModel(@NonNull List tModels, InternalAdapter adapter, DatabaseWrapper wrapper) { - adapter.deleteAll(tModels, wrapper); - } - }, internalAdapter); - } - - /** - * Description: Simple interface for acting on a model in a Transaction or list of {@link Model} - */ - interface ProcessModelList { - - /** - * Called when processing models - * - * @param modelList The model list to process - */ - void processModel(@NonNull List modelList, InternalAdapter adapter, - DatabaseWrapper wrapper); - } - - final List models; - final ProcessModelList processModelList; - final InternalAdapter internalAdapter; - - FastStoreModelTransaction(Builder builder) { - models = builder.models; - processModelList = builder.processModelList; - internalAdapter = builder.internalAdapter; - } - - @Override - public void execute(DatabaseWrapper databaseWrapper) { - if (models != null) { - processModelList.processModel(models, internalAdapter, databaseWrapper); - } - } - - /** - * Makes it easy to build a {@link ProcessModelTransaction}. - * - * @param - */ - public static final class Builder { - - private final ProcessModelList processModelList; - @NonNull private final InternalAdapter internalAdapter; - List models = new ArrayList<>(); - - Builder(@NonNull ProcessModelList processModelList, - @NonNull InternalAdapter internalAdapter) { - this.processModelList = processModelList; - this.internalAdapter = internalAdapter; - } - - @NonNull - public Builder add(TModel model) { - models.add(model); - return this; - } - - /** - * Adds all specified models to the {@link ArrayList}. - */ - @NonNull - @SafeVarargs - public final Builder addAll(TModel... models) { - this.models.addAll(Arrays.asList(models)); - return this; - } - - /** - * Adds a {@link Collection} of {@link Model} to the existing {@link ArrayList}. - */ - @NonNull - public Builder addAll(Collection models) { - if (models != null) { - this.models.addAll(models); - } - return this; - } - - /** - * @return A new {@link ProcessModelTransaction}. Subsequent calls to this method produce - * new instances. - */ - @NonNull - public FastStoreModelTransaction build() { - return new FastStoreModelTransaction<>(this); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/ITransaction.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/ITransaction.java deleted file mode 100644 index 292a645a1..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/ITransaction.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.database.sqlite.SQLiteDatabaseLockedException; - -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -/** - * Description: Simplest form of a transaction. It represents an interface by which code is executed - * inside a database transaction. - */ -public interface ITransaction { - - /** - * Called within a database transaction. - * - * @param databaseWrapper The database to save data into. Use this access to operate on the DB - * without causing {@link SQLiteDatabaseLockedException}. - */ - void execute(DatabaseWrapper databaseWrapper); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/PriorityTransactionQueue.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/PriorityTransactionQueue.java deleted file mode 100644 index 280e5e431..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/PriorityTransactionQueue.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.os.Looper; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.config.FlowLog; - -import java.util.Iterator; -import java.util.concurrent.PriorityBlockingQueue; - -/** - * Description: Orders {@link Transaction} in a priority-based queue, enabling you perform higher priority - * tasks first (such as retrievals) if you are attempting many DB operations at once. - */ -public class PriorityTransactionQueue extends Thread implements ITransactionQueue { - - private final PriorityBlockingQueue> queue; - - private boolean isQuitting = false; - - /** - * Creates a queue with the specified name to ID it. - * - * @param name - */ - public PriorityTransactionQueue(String name) { - super(name); - queue = new PriorityBlockingQueue<>(); - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - Looper.prepare(); - android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); - PriorityEntry transaction; - while (true) { - try { - transaction = queue.take(); - } catch (InterruptedException e) { - if (isQuitting) { - synchronized (queue) { - queue.clear(); - } - return; - } - continue; - } - - transaction.entry.executeSync(); - } - } - - @Override - public void add(@NonNull Transaction transaction) { - synchronized (queue) { - PriorityEntry priorityEntry = new PriorityEntry<>(transaction); - if (!queue.contains(priorityEntry)) { - queue.add(priorityEntry); - } - } - } - - /** - * Cancels the specified request. - * - * @param transaction The transaction to cancel (if still in the queue). - */ - @Override - public void cancel(@NonNull Transaction transaction) { - synchronized (queue) { - PriorityEntry priorityEntry = new PriorityEntry<>(transaction); - if (queue.contains(priorityEntry)) { - queue.remove(priorityEntry); - } - } - } - - /** - * Cancels all requests by a specific tag - * - * @param tag - */ - @Override - public void cancel(@NonNull String tag) { - synchronized (queue) { - Iterator> it = queue.iterator(); - while (it.hasNext()) { - Transaction next = it.next().entry; - if (next.name() != null && next.name().equals(tag)) { - it.remove(); - } - } - } - } - - @Override - public void startIfNotAlive() { - synchronized (this) { - if (!isAlive()) { - try { - start(); - } catch (IllegalThreadStateException i) { - // log if failure from thread is still alive. - FlowLog.log(FlowLog.Level.E, i); - } - } - } - } - - /** - * Quits this process - */ - @Override - public void quit() { - isQuitting = true; - interrupt(); - } - - private void throwInvalidTransactionType(Transaction transaction) { - throw new IllegalArgumentException("Transaction of type:" + - (transaction != null ? transaction.transaction().getClass() : "Unknown") - + " should be of type PriorityTransactionWrapper"); - } - - class PriorityEntry - implements Comparable> { - final E entry; - final PriorityTransactionWrapper transactionWrapper; - - public PriorityEntry(E entry) { - this.entry = entry; - if (entry.transaction() instanceof PriorityTransactionWrapper) { - transactionWrapper = ((PriorityTransactionWrapper) entry.transaction()); - } else { - transactionWrapper = new PriorityTransactionWrapper.Builder(entry.transaction()) - .build(); - } - } - - public E getEntry() { - return entry; - } - - @Override - public int compareTo(@NonNull PriorityEntry another) { - return transactionWrapper.compareTo(another.transactionWrapper); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - PriorityEntry that = (PriorityEntry) o; - - return transactionWrapper != null ? transactionWrapper.equals(that.transactionWrapper) - : that.transactionWrapper == null; - - } - - @Override - public int hashCode() { - return transactionWrapper != null ? transactionWrapper.hashCode() : 0; - } - } - - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/PriorityTransactionWrapper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/PriorityTransactionWrapper.java deleted file mode 100644 index 84902201d..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/PriorityTransactionWrapper.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.support.annotation.IntDef; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Description: Provides transaction with priority. Meant to be used in a {@link PriorityTransactionQueue}. - */ -public class PriorityTransactionWrapper implements ITransaction, Comparable { - - @Retention(RetentionPolicy.SOURCE) - @IntDef({PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH, PRIORITY_UI}) - public @interface Priority { - } - - /** - * Low priority requests, reserved for non-essential tasks - */ - public static final int PRIORITY_LOW = 0; - - /** - * The main of the requests, good for when adding a bunch of - * data to the DB that the app does not access right away (default). - */ - public static final int PRIORITY_NORMAL = 1; - - /** - * Reserved for tasks that will influence user interaction, such as displaying data in the UI - * some point in the future (not necessarily right away) - */ - public static final int PRIORITY_HIGH = 2; - - /** - * Reserved for only immediate tasks and all forms of fetching that will display on the UI - */ - public static final int PRIORITY_UI = 5; - - private final int priority; - private final ITransaction transaction; - - PriorityTransactionWrapper(Builder builder) { - if (builder.priority == 0) { - priority = PRIORITY_NORMAL; - } else { - priority = builder.priority; - } - transaction = builder.transaction; - } - - @Override - public void execute(DatabaseWrapper databaseWrapper) { - transaction.execute(databaseWrapper); - } - - @Override - public int compareTo(@NonNull PriorityTransactionWrapper another) { - return another.priority - priority; - } - - public static class Builder { - - private final ITransaction transaction; - private int priority; - - public Builder(@NonNull ITransaction transaction) { - this.transaction = transaction; - } - - /** - * Sets a {@link Priority} that orders this transaction. - */ - public Builder priority(@Priority int priority) { - this.priority = priority; - return this; - } - - public PriorityTransactionWrapper build() { - return new PriorityTransactionWrapper(this); - } - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/ProcessModelTransaction.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/ProcessModelTransaction.java deleted file mode 100644 index 3ae0c3bd7..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/ProcessModelTransaction.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * Description: Allows you to process a single or {@link List} of models in a transaction. You - * can operate on a set of {@link Model} to {@link Model#save()}, {@link Model#update()}, etc. - */ -public class ProcessModelTransaction implements ITransaction { - - - /** - * Description: Simple interface for acting on a model in a Transaction or list of {@link Model} - */ - public interface ProcessModel { - - /** - * Called when processing models - * - * @param model The model to process - * @param wrapper - */ - void processModel(TModel model, DatabaseWrapper wrapper); - } - - /** - * Listener for providing callbacks as models are processed in this {@link ITransaction}. - * - * @param The model class. - */ - public interface OnModelProcessListener { - - /** - * Called when model has been operated on. - * - * @param current The current index of items processed. - * @param total The total number of items to process. - * @param modifiedModel The model previously modified. - */ - void onModelProcessed(long current, long total, TModel modifiedModel); - } - - final OnModelProcessListener processListener; - final List models; - final ProcessModel processModel; - final boolean runProcessListenerOnSameThread; - - ProcessModelTransaction(Builder builder) { - processListener = builder.processListener; - models = builder.models; - processModel = builder.processModel; - runProcessListenerOnSameThread = builder.runProcessListenerOnSameThread; - } - - @Override - public void execute(DatabaseWrapper databaseWrapper) { - if (models != null) { - final int size = models.size(); - for (int i = 0; i < size; i++) { - final TModel model = models.get(i); - processModel.processModel(model, databaseWrapper); - - if (processListener != null) { - if (runProcessListenerOnSameThread) { - processListener.onModelProcessed(i, size, model); - } else { - final int finalI = i; - Transaction.getTransactionHandler().post(new Runnable() { - @Override - public void run() { - processListener.onModelProcessed(finalI, size, model); - } - }); - } - } - } - } - } - - /** - * Makes it easy to build a {@link ProcessModelTransaction}. - * - * @param - */ - public static final class Builder { - - private final ProcessModel processModel; - OnModelProcessListener processListener; - List models = new ArrayList<>(); - private boolean runProcessListenerOnSameThread; - - - public Builder(@NonNull ProcessModel processModel) { - this.processModel = processModel; - } - - /** - * @param models The models to process. This constructor creates a new {@link ArrayList} - * from the {@link Collection} passed. - * @param processModel The method call interface. - */ - public Builder(Collection models, @NonNull ProcessModel processModel) { - this.processModel = processModel; - this.models = new ArrayList<>(models); - } - - public Builder add(TModel model) { - models.add(model); - return this; - } - - /** - * Adds all specified models to the {@link ArrayList}. - */ - @SafeVarargs - public final Builder addAll(TModel... models) { - this.models.addAll(Arrays.asList(models)); - return this; - } - - /** - * Adds a {@link Collection} of {@link Model} to the existing {@link ArrayList}. - */ - public Builder addAll(Collection models) { - if (models != null) { - this.models.addAll(models); - } - return this; - } - - /** - * @param processListener Allows you to listen for when models are processed to update UI, - * this is called on the UI thread. - */ - public Builder processListener(OnModelProcessListener processListener) { - this.processListener = processListener; - return this; - } - - /** - * @param runProcessListenerOnSameThread Default is false. If true we return callback - * on same calling thread, if false we push the callback - * to the UI thread. - */ - public Builder runProcessListenerOnSameThread(boolean runProcessListenerOnSameThread) { - this.runProcessListenerOnSameThread = runProcessListenerOnSameThread; - return this; - } - - /** - * @return A new {@link ProcessModelTransaction}. Subsequent calls to this method produce - * new instances. - */ - public ProcessModelTransaction build() { - return new ProcessModelTransaction<>(this); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/QueryTransaction.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/QueryTransaction.java deleted file mode 100644 index f96b7e562..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/QueryTransaction.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.language.CursorResult; -import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.List; - -/** - * Description: Provides an easy way to query for data asynchronously. - */ -public class QueryTransaction implements ITransaction { - - /** - * Simple interface that provides callback on result. - * - * @param The result that we got from querying. - */ - public interface QueryResultCallback { - - /** - * Called when the query completes. - * - * @param transaction The transaction that ran. - * @param tResult The result of the query. Use this object to get data that you need. - */ - void onQueryResult(@NonNull QueryTransaction transaction, - @NonNull CursorResult tResult); - } - - /** - * Simple interface that provides {@link List} callback on result. - * - * @param The result that we got from querying. - */ - public interface QueryResultListCallback { - - /** - * Called when the query completes. - * - * @param transaction The transaction that ran. - * @param tResult The {@link List} result of the query. - */ - void onListQueryResult(QueryTransaction transaction, @NonNull List tResult); - } - - /** - * Simple interface that provides single {@link TResult} callback on result. - * - * @param The result that we got from querying. - */ - public interface QueryResultSingleCallback { - - /** - * Called when the query completes. - * - * @param transaction The transaction that ran. - * @param tResult The single result of the query. - */ - void onSingleQueryResult(QueryTransaction transaction, @Nullable TResult tResult); - } - - final ModelQueriable modelQueriable; - final QueryResultCallback queryResultCallback; - final QueryResultListCallback queryResultListCallback; - final QueryResultSingleCallback queryResultSingleCallback; - final boolean runResultCallbacksOnSameThread; - - QueryTransaction(Builder builder) { - modelQueriable = builder.modelQueriable; - queryResultCallback = builder.queryResultCallback; - queryResultListCallback = builder.queryResultListCallback; - queryResultSingleCallback = builder.queryResultSingleCallback; - runResultCallbacksOnSameThread = builder.runResultCallbacksOnSameThread; - } - - @Override - public void execute(DatabaseWrapper databaseWrapper) { - final CursorResult cursorResult = modelQueriable.queryResults(); - if (queryResultCallback != null) { - if (runResultCallbacksOnSameThread) { - queryResultCallback.onQueryResult(this, cursorResult); - } else { - Transaction.getTransactionHandler().post(new Runnable() { - @Override - public void run() { - queryResultCallback.onQueryResult(QueryTransaction.this, cursorResult); - } - }); - } - } - - if (queryResultListCallback != null) { - final List resultList = cursorResult.toListClose(); - if (runResultCallbacksOnSameThread) { - queryResultListCallback.onListQueryResult(this, resultList); - } else { - Transaction.getTransactionHandler().post(new Runnable() { - @Override - public void run() { - queryResultListCallback.onListQueryResult(QueryTransaction.this, resultList); - } - }); - } - } - - if (queryResultSingleCallback != null) { - final TResult result = cursorResult.toModelClose(); - if (runResultCallbacksOnSameThread) { - queryResultSingleCallback.onSingleQueryResult(this, result); - } else { - Transaction.getTransactionHandler().post(new Runnable() { - @Override - public void run() { - queryResultSingleCallback.onSingleQueryResult(QueryTransaction.this, result); - } - }); - } - } - } - - /** - * Provides easy way to build a {@link QueryTransaction}. - * - * @param - */ - public static final class Builder { - - final ModelQueriable modelQueriable; - QueryResultCallback queryResultCallback; - QueryResultListCallback queryResultListCallback; - QueryResultSingleCallback queryResultSingleCallback; - boolean runResultCallbacksOnSameThread; - - public Builder(@NonNull ModelQueriable modelQueriable) { - this.modelQueriable = modelQueriable; - } - - /** - * Called when transaction completes and use this to get results. - */ - public Builder queryResult(QueryResultCallback queryResultCallback) { - this.queryResultCallback = queryResultCallback; - return this; - } - - /** - * Called when transaction completes, which returns a {@link List} result. - */ - public Builder queryListResult(QueryResultListCallback queryResultListCallback) { - this.queryResultListCallback = queryResultListCallback; - return this; - } - - /** - * Called when transaction completes, which returns a single {@link TResult}. - */ - public Builder querySingleResult(QueryResultSingleCallback queryResultSingleCallback) { - this.queryResultSingleCallback = queryResultSingleCallback; - return this; - } - - /** - * Runs result callback on UI thread by default. setting this to true would run the callback - * synchrously on the same thread this transaction executes from. - */ - public Builder runResultCallbacksOnSameThread(boolean runResultCallbacksOnSameThread) { - this.runResultCallbacksOnSameThread = runResultCallbacksOnSameThread; - return this; - } - - /** - * @return A new {@link QueryTransaction}. Subsequent calls to this method produce new - * instances. - */ - public QueryTransaction build() { - return new QueryTransaction<>(this); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/Transaction.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/Transaction.java deleted file mode 100644 index 5f5a4bd64..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/Transaction.java +++ /dev/null @@ -1,262 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import android.os.Handler; -import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.DatabaseDefinition; -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.runtime.BaseTransactionManager; - -/** - * Description: The main transaction class. It represents a transaction that occurs in the database. - * This is a handy class that allows you to wrap up a set of database modification (or queries) into - * a code block that gets accessed all on the same thread, in the same queue. This can prevent locking - * and synchronization issues when trying to read and write from the database at the same time. - *

- * To create one, the recommended method is to use the {@link DatabaseDefinition#beginTransactionAsync(ITransaction)}. - */ -public final class Transaction { - - /** - * Callback when a {@link ITransaction} failed because of an exception. - */ - public interface Error { - - /** - * Called when transaction fails. - * - * @param transaction The transaction that failed. - * @param error The error that was thrown. - */ - void onError(@NonNull Transaction transaction, - @NonNull Throwable error); - } - - /** - * Interface callback when a {@link ITransaction} was successful. - */ - public interface Success { - - /** - * Called when a transaction succeeded. - * - * @param transaction The transaction that succeeded. - */ - void onSuccess(@NonNull Transaction transaction); - } - - private static Handler TRANSACTION_HANDLER; - - static Handler getTransactionHandler() { - if (TRANSACTION_HANDLER == null) { - TRANSACTION_HANDLER = new Handler(Looper.getMainLooper()); - } - return TRANSACTION_HANDLER; - } - - - final Error errorCallback; - final Success successCallback; - final ITransaction transaction; - final DatabaseDefinition databaseDefinition; - final String name; - final boolean shouldRunInTransaction; - final boolean runCallbacksOnSameThread; - - - Transaction(Builder builder) { - databaseDefinition = builder.databaseDefinition; - errorCallback = builder.errorCallback; - successCallback = builder.successCallback; - transaction = builder.transaction; - name = builder.name; - shouldRunInTransaction = builder.shouldRunInTransaction; - runCallbacksOnSameThread = builder.runCallbacksOnSameThread; - } - - @Nullable - public Error error() { - return errorCallback; - } - - @Nullable - public Success success() { - return successCallback; - } - - @NonNull - public ITransaction transaction() { - return transaction; - } - - @Nullable - public String name() { - return name; - } - - /** - * Runs the transaction in the {@link BaseTransactionManager} of the associated database. - */ - public void execute() { - databaseDefinition.getTransactionManager().addTransaction(this); - } - - /** - * Cancels a transaction that has not run yet. - */ - public void cancel() { - databaseDefinition.getTransactionManager().cancelTransaction(this); - } - - /** - * Executes the transaction immediately on the same thread from which it is called. This calls - * the {@link DatabaseDefinition#executeTransaction(ITransaction)} method, which runs the - * {@link #transaction()} in a database transaction. - */ - public void executeSync() { - try { - if (shouldRunInTransaction) { - databaseDefinition.executeTransaction(transaction); - } else { - transaction.execute(databaseDefinition.getWritableDatabase()); - } - if (successCallback != null) { - if (runCallbacksOnSameThread) { - successCallback.onSuccess(this); - } else { - getTransactionHandler().post(new Runnable() { - @Override - public void run() { - successCallback.onSuccess(Transaction.this); - } - }); - } - } - } catch (final Throwable throwable) { - FlowLog.logError(throwable); - if (errorCallback != null) { - if (runCallbacksOnSameThread) { - errorCallback.onError(this, throwable); - } else { - getTransactionHandler().post(new Runnable() { - @Override - public void run() { - errorCallback.onError(Transaction.this, throwable); - } - }); - } - } else { - throw new RuntimeException("An exception occurred while executing a transaction", throwable); - } - } - } - - @NonNull - public Builder newBuilder() { - return new Builder(transaction, databaseDefinition) - .error(errorCallback) - .success(successCallback) - .name(name) - .shouldRunInTransaction(shouldRunInTransaction) - .runCallbacksOnSameThread(runCallbacksOnSameThread); - } - - /** - * The main entry point into {@link Transaction}, this provides an easy way to build up transactions. - */ - public static final class Builder { - - final ITransaction transaction; - @NonNull final DatabaseDefinition databaseDefinition; - Error errorCallback; - Success successCallback; - String name; - boolean shouldRunInTransaction = true; - private boolean runCallbacksOnSameThread; - - - /** - * @param transaction The interface that actually executes the transaction. - * @param databaseDefinition The database this transaction will run on. Should be the same - * DB as the code that the transaction runs in. - */ - public Builder(@NonNull ITransaction transaction, @NonNull DatabaseDefinition databaseDefinition) { - this.transaction = transaction; - this.databaseDefinition = databaseDefinition; - } - - /** - * Specify an error callback to return all and any {@link Throwable} that occured during a {@link Transaction}. - */ - @NonNull - public Builder error(@Nullable Error errorCallback) { - this.errorCallback = errorCallback; - return this; - } - - /** - * Specify a listener for successful transactions. This is called when the {@link ITransaction} - * specified is finished and it is posted on the UI thread. - * - * @param successCallback The callback, invoked on the UI thread. - */ - @NonNull - public Builder success(@Nullable Success successCallback) { - this.successCallback = successCallback; - return this; - } - - /** - * Give this transaction a name. This will allow you to call {@link ITransactionQueue#cancel(String)}. - * - * @param name The name of this transaction. Should be unique for any transaction currently - * running in the {@link ITransactionQueue}. - */ - @NonNull - public Builder name(@Nullable String name) { - this.name = name; - return this; - } - - /** - * @param shouldRunInTransaction True is default. If true, we run this {@link Transaction} in - * a database transaction. If this is not necessary (usually for - * {@link QueryTransaction}), you should specify false. - * @return - */ - @NonNull - public Builder shouldRunInTransaction(boolean shouldRunInTransaction) { - this.shouldRunInTransaction = shouldRunInTransaction; - return this; - } - - /** - * @param runCallbacksOnSameThread Default is false. If true we return the callbacks from - * this {@link Transaction} on the same thread we call - * {@link #execute()} from. - */ - @NonNull - public Builder runCallbacksOnSameThread(boolean runCallbacksOnSameThread) { - this.runCallbacksOnSameThread = runCallbacksOnSameThread; - return this; - } - - /** - * @return A new instance of {@link Transaction}. Subsequent calls to this method produce - * new instances. - */ - @NonNull - public Transaction build() { - return new Transaction(this); - } - - /** - * Convenience method to simply execute a transaction. - */ - public void execute() { - build().execute(); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/TransactionWrapper.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/TransactionWrapper.java deleted file mode 100644 index 4ef0b1bc7..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/transaction/TransactionWrapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.raizlabs.android.dbflow.structure.database.transaction; - -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * Description: Wraps multiple transactions together. - */ -public class TransactionWrapper implements ITransaction { - - private final List transactions = new ArrayList<>(); - - public TransactionWrapper(ITransaction... transactions) { - this.transactions.addAll(Arrays.asList(transactions)); - } - - public TransactionWrapper(Collection transactions) { - this.transactions.addAll(transactions); - } - - @Override - public void execute(DatabaseWrapper databaseWrapper) { - for (ITransaction transaction : transactions) { - transaction.execute(databaseWrapper); - } - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/ContentValuesListener.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/ContentValuesListener.java deleted file mode 100644 index 813059056..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/ContentValuesListener.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.raizlabs.android.dbflow.structure.listener; - -import android.content.ContentValues; - -import com.raizlabs.android.dbflow.annotation.provider.ContentProvider; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.ModelAdapter; - -/** - * Description: Called after the declared {@link ContentValues} are binded. It enables - * us to listen and add custom behavior to the {@link ContentValues}. These must be - * defined in a {@link Model} class to register properly. - *

- * This class will no longer get called during updates unless explicit call to - * {@link ModelAdapter#bindToContentValues(ContentValues, Object)} - * or {@link ModelAdapter#bindToInsertValues(ContentValues, Object)} - * - * @see SQLiteStatementListener - */ -@Deprecated -public interface ContentValuesListener { - - /** - * Called during an {@link Model#update()} and at the end of - * {@link ModelAdapter#bindToContentValues(ContentValues, Object)} - * . It enables you to customly change the values as necessary during update to the database. - * - * @param contentValues The content values to bind to for an update. - */ - void onBindToContentValues(ContentValues contentValues); - - /** - * Called during an {@link Model#update()} and at the end of - * {@link ModelAdapter#bindToInsertValues(ContentValues, Object)}. - * It enables you to customly change the values as necessary during insert - * to the database for a {@link ContentProvider}. - * - * @param contentValues The content values to insert into DB for a {@link ContentProvider} - */ - void onBindToInsertValues(ContentValues contentValues); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/LoadFromCursorListener.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/LoadFromCursorListener.java deleted file mode 100644 index e0d52053a..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/LoadFromCursorListener.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.raizlabs.android.dbflow.structure.listener; - -import android.database.Cursor; -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.Model; - -/** - * Description: Marks a {@link Model} as listening to {@link Cursor} - * events for custom handling when loading from the DB. - */ -public interface LoadFromCursorListener { - - /** - * Called when the {@link Model} is loaded from the DB. - * - * @param cursor The cursor that is loaded. - */ - void onLoadFromCursor(@NonNull Cursor cursor); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/SQLiteStatementListener.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/SQLiteStatementListener.java deleted file mode 100644 index 788f89a6c..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/listener/SQLiteStatementListener.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.raizlabs.android.dbflow.structure.listener; - -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.InternalAdapter; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.DatabaseStatement; - -/** - * Description: Marks a {@link Model} as subscribing to the {@link DatabaseStatement} - * that is used to {@link Model#insert()} a model into the DB. - */ -public interface SQLiteStatementListener { - - /** - * Called at the end of {@link InternalAdapter#bindToStatement(DatabaseStatement, Object)} - * Perform a custom manipulation of the statement as willed. - * - * @param databaseStatement The statement from the {@link ModelAdapter} - */ - void onBindToStatement(@NonNull DatabaseStatement databaseStatement); - - /** - * Called at the end of {@link InternalAdapter#bindToInsertStatement(DatabaseStatement, Object)} - * Perform a custom manipulation of the statement as willed. - * - * @param databaseStatement The insert statement from the {@link ModelAdapter} - */ - void onBindToInsertStatement(@NonNull DatabaseStatement databaseStatement); - - /** - * Called at the end of {@link InternalAdapter#bindToUpdateStatement(DatabaseStatement, Object)} - * Perform a custom manipulation of the statement as willed. - * - * @param databaseStatement The insert statement from the {@link ModelAdapter} - */ - void onBindToUpdateStatement(@NonNull DatabaseStatement databaseStatement); - - void onBindToDeleteStatement(@NonNull DatabaseStatement databaseStatement); -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseProviderModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseProviderModel.java deleted file mode 100644 index 99442795f..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseProviderModel.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.raizlabs.android.dbflow.structure.provider; - -import android.content.ContentProvider; -import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.language.OperatorGroup; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: Provides a base implementation of a {@link Model} backed - * by a content provider. All model operations are overridden using the {@link ContentUtils}. - * Consider using a {@link BaseSyncableProviderModel} if you wish to - * keep modifications locally from the {@link ContentProvider} - */ -public abstract class BaseProviderModel - extends BaseModel implements ModelProvider { - - @Override - public boolean delete() { - return ContentUtils.delete(getDeleteUri(), this) > 0; - } - - @Override - public boolean save() { - int count = ContentUtils.update(getUpdateUri(), this); - if (count == 0) { - return ContentUtils.insert(getInsertUri(), this) != null; - } else { - return count > 0; - } - } - - @Override - public boolean update() { - return ContentUtils.update(getUpdateUri(), this) > 0; - } - - @Override - public long insert() { - ContentUtils.insert(getInsertUri(), this); - return 0; - } - - /** - * Runs a query on the {@link ContentProvider} to see if it returns data. - * - * @return true if this model exists in the {@link ContentProvider} based on its primary keys. - */ - @Override - @SuppressWarnings("unchecked") - public boolean exists() { - Cursor cursor = ContentUtils.query(FlowManager.getContext().getContentResolver(), - getQueryUri(), getModelAdapter().getPrimaryConditionClause(this), ""); - boolean exists = (cursor != null && cursor.getCount() > 0); - if (cursor != null) { - cursor.close(); - } - return exists; - } - - @Override - @SuppressWarnings("unchecked") - public void load(@NonNull OperatorGroup whereConditions, - @Nullable String orderBy, String... columns) { - FlowCursor cursor = FlowCursor.from(ContentUtils.query(FlowManager.getContext().getContentResolver(), - getQueryUri(), whereConditions, orderBy, columns)); - if (cursor != null && cursor.moveToFirst()) { - getModelAdapter().loadFromCursor(cursor, this); - cursor.close(); - } - } - - @Override - @SuppressWarnings("unchecked") - public void load() { - load(getModelAdapter().getPrimaryConditionClause(this), ""); - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseSyncableProviderModel.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseSyncableProviderModel.java deleted file mode 100644 index 8699e99d8..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/BaseSyncableProviderModel.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.raizlabs.android.dbflow.structure.provider; - -import android.content.ContentProvider; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.language.OperatorGroup; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.Model; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -/** - * Description: Provides a base implementation of a {@link Model} backed - * by a content provider. All operations sync with the content provider in this app from a {@link ContentProvider} - */ -public abstract class BaseSyncableProviderModel extends BaseModel implements ModelProvider { - - @Override - public long insert() { - long rowId = super.insert(); - ContentUtils.insert(getInsertUri(), this); - return rowId; - } - - @Override - public boolean save() { - if (exists()) { - return super.save() && ContentUtils.update(getUpdateUri(), this) > 0; - } else { - return super.save() && ContentUtils.insert(getInsertUri(), this) != null; - } - } - - @Override - public boolean delete() { - return super.delete() && ContentUtils.delete(getDeleteUri(), this) > 0; - } - - @Override - public boolean update() { - return super.update() && ContentUtils.update(getUpdateUri(), this) > 0; - } - - @Override - @SuppressWarnings("unchecked") - public void load(@NonNull OperatorGroup whereOperatorGroup, - @Nullable String orderBy, String... columns) { - FlowCursor cursor = FlowCursor.from(ContentUtils.query(FlowManager.getContext().getContentResolver(), - getQueryUri(), whereOperatorGroup, orderBy, columns)); - if (cursor != null && cursor.moveToFirst()) { - getModelAdapter().loadFromCursor(cursor, this); - cursor.close(); - } - } - - @Override - @SuppressWarnings("unchecked") - public void load() { - load(getModelAdapter().getPrimaryConditionClause(this), ""); - } -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java deleted file mode 100644 index 31902791e..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ContentUtils.java +++ /dev/null @@ -1,296 +0,0 @@ -package com.raizlabs.android.dbflow.structure.provider; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; - -import com.raizlabs.android.dbflow.annotation.provider.ContentProvider; -import com.raizlabs.android.dbflow.config.FlowLog; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.sql.language.Operator; -import com.raizlabs.android.dbflow.sql.language.OperatorGroup; -import com.raizlabs.android.dbflow.structure.ModelAdapter; -import com.raizlabs.android.dbflow.structure.database.FlowCursor; - -import java.util.ArrayList; -import java.util.List; - -/** - * Description: Provides handy wrapper mechanisms for {@link android.content.ContentProvider} - */ -public class ContentUtils { - - /** - * The default content URI that Android recommends. Not necessary, however. - */ - public static final String BASE_CONTENT_URI = "content://"; - - /** - * Constructs an Uri with the {@link #BASE_CONTENT_URI} and authority. Add paths to append to the Uri. - * - * @param authority The authority for a {@link ContentProvider} - * @param paths The list of paths to append. - * @return A complete Uri for a {@link ContentProvider} - */ - public static Uri buildUriWithAuthority(String authority, String... paths) { - return buildUri(BASE_CONTENT_URI, authority, paths); - } - - /** - * Constructs an Uri with the specified baseContent uri and authority. Add paths to append to the Uri. - * - * @param baseContentUri The base content URI for a {@link ContentProvider} - * @param authority The authority for a {@link ContentProvider} - * @param paths The list of paths to append. - * @return A complete Uri for a {@link ContentProvider} - */ - public static Uri buildUri(String baseContentUri, String authority, String... paths) { - Uri.Builder builder = Uri.parse(baseContentUri + authority).buildUpon(); - for (String path : paths) { - builder.appendPath(path); - } - return builder.build(); - } - - /** - * Inserts the model into the {@link android.content.ContentResolver}. Uses the insertUri to resolve - * the reference and the model to convert its data into {@link android.content.ContentValues} - * - * @param insertUri A {@link android.net.Uri} from the {@link ContentProvider} class definition. - * @param model The model to insert. - * @return A Uri of the inserted data. - */ - public static Uri insert(Uri insertUri, TableClass model) { - return insert(FlowManager.getContext().getContentResolver(), insertUri, model); - } - - /** - * Inserts the model into the {@link android.content.ContentResolver}. Uses the insertUri to resolve - * the reference and the model to convert its data into {@link android.content.ContentValues} - * - * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) - * @param insertUri A {@link android.net.Uri} from the {@link ContentProvider} class definition. - * @param model The model to insert. - * @return The Uri of the inserted data. - */ - @SuppressWarnings("unchecked") - public static Uri insert(ContentResolver contentResolver, Uri insertUri, TableClass model) { - ModelAdapter adapter = (ModelAdapter) FlowManager.getModelAdapter(model.getClass()); - - ContentValues contentValues = new ContentValues(); - adapter.bindToInsertValues(contentValues, model); - Uri uri = contentResolver.insert(insertUri, contentValues); - adapter.updateAutoIncrement(model, Long.valueOf(uri.getPathSegments().get(uri.getPathSegments().size() - 1))); - return uri; - } - - /** - * Inserts the list of model into the {@link ContentResolver}. Binds all of the models to {@link ContentValues} - * and runs the {@link ContentResolver#bulkInsert(Uri, ContentValues[])} method. Note: if any of these use - * autoIncrementing primary keys the ROWID will not be properly updated from this method. If you care - * use {@link #insert(ContentResolver, Uri, Object)} instead. - * - * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) - * @param bulkInsertUri The URI to bulk insert with - * @param table The table to insert into - * @param models The models to insert. - * @return The count of the rows affected by the insert. - */ - public static int bulkInsert(ContentResolver contentResolver, Uri bulkInsertUri, - Class table, List models) { - ContentValues[] contentValues = new ContentValues[models == null ? 0 : models.size()]; - ModelAdapter adapter = FlowManager.getModelAdapter(table); - - if (models != null) { - for (int i = 0; i < contentValues.length; i++) { - contentValues[i] = new ContentValues(); - adapter.bindToInsertValues(contentValues[i], models.get(i)); - } - } - return contentResolver.bulkInsert(bulkInsertUri, contentValues); - } - - /** - * Inserts the list of model into the {@link ContentResolver}. Binds all of the models to {@link ContentValues} - * and runs the {@link ContentResolver#bulkInsert(Uri, ContentValues[])} method. Note: if any of these use - * autoincrement primary keys the ROWID will not be properly updated from this method. If you care - * use {@link #insert(Uri, Object)} instead. - * - * @param bulkInsertUri The URI to bulk insert with - * @param table The table to insert into - * @param models The models to insert. - * @return The count of the rows affected by the insert. - */ - public static int bulkInsert(Uri bulkInsertUri, Class table, List models) { - return bulkInsert(FlowManager.getContext().getContentResolver(), bulkInsertUri, table, models); - } - - /** - * Updates the model through the {@link android.content.ContentResolver}. Uses the updateUri to - * resolve the reference and the model to convert its data in {@link android.content.ContentValues} - * - * @param updateUri A {@link android.net.Uri} from the {@link ContentProvider} - * @param model A model to update - * @return The number of rows updated. - */ - public static int update(Uri updateUri, TableClass model) { - return update(FlowManager.getContext().getContentResolver(), updateUri, model); - } - - /** - * Updates the model through the {@link android.content.ContentResolver}. Uses the updateUri to - * resolve the reference and the model to convert its data in {@link android.content.ContentValues} - * - * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) - * @param updateUri A {@link android.net.Uri} from the {@link ContentProvider} - * @param model The model to update - * @return The number of rows updated. - */ - @SuppressWarnings("unchecked") - public static int update(ContentResolver contentResolver, Uri updateUri, TableClass model) { - ModelAdapter adapter = (ModelAdapter) FlowManager.getModelAdapter(model.getClass()); - - ContentValues contentValues = new ContentValues(); - adapter.bindToContentValues(contentValues, model); - int count = contentResolver.update(updateUri, contentValues, adapter.getPrimaryConditionClause(model).getQuery(), null); - if (count == 0) { - FlowLog.log(FlowLog.Level.W, "Updated failed of: " + model.getClass()); - } - return count; - } - - /** - * Deletes the specified model through the {@link android.content.ContentResolver}. Uses the deleteUri - * to resolve the reference and the model to {@link ModelAdapter#getPrimaryConditionClause(Object)}} - * - * @param deleteUri A {@link android.net.Uri} from the {@link ContentProvider} - * @param model The model to delete - * @return The number of rows deleted. - */ - @SuppressWarnings("unchecked") - public static int delete(Uri deleteUri, TableClass model) { - return delete(FlowManager.getContext().getContentResolver(), deleteUri, model); - } - - /** - * Deletes the specified model through the {@link android.content.ContentResolver}. Uses the deleteUri - * to resolve the reference and the model to {@link ModelAdapter#getPrimaryConditionClause(Object)} - * - * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) - * @param deleteUri A {@link android.net.Uri} from the {@link ContentProvider} - * @param model The model to delete - * @return The number of rows deleted. - */ - @SuppressWarnings("unchecked") - public static int delete(ContentResolver contentResolver, Uri deleteUri, TableClass model) { - ModelAdapter adapter = (ModelAdapter) FlowManager.getModelAdapter(model.getClass()); - - int count = contentResolver.delete(deleteUri, adapter.getPrimaryConditionClause(model).getQuery(), null); - - // reset autoincrement to 0 - if (count > 0) { - adapter.updateAutoIncrement(model, 0); - } else { - FlowLog.log(FlowLog.Level.W, "A delete on " + model.getClass() + " within the ContentResolver appeared to fail."); - } - return count; - } - - /** - * Queries the {@link android.content.ContentResolver} with the specified query uri. It generates - * the correct query and returns a {@link android.database.Cursor} - * - * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) - * @param queryUri The URI of the query - * @param whereConditions The set of {@link Operator} to query the content provider. - * @param orderBy The order by clause without the ORDER BY - * @param columns The list of columns to query. - * @return A {@link android.database.Cursor} - */ - public static Cursor query(ContentResolver contentResolver, Uri queryUri, - OperatorGroup whereConditions, - String orderBy, String... columns) { - return contentResolver.query(queryUri, columns, whereConditions.getQuery(), null, orderBy); - } - - /** - * Queries the {@link android.content.ContentResolver} with the specified queryUri. It will generate - * the correct query and return a list of {@link TableClass} - * - * @param queryUri The URI of the query - * @param table The table to get from. - * @param whereConditions The set of {@link Operator} to query the content provider. - * @param orderBy The order by clause without the ORDER BY - * @param columns The list of columns to query. - * @return A list of {@link TableClass} - */ - public static List queryList(Uri queryUri, Class table, - OperatorGroup whereConditions, - String orderBy, String... columns) { - return queryList(FlowManager.getContext().getContentResolver(), queryUri, table, whereConditions, orderBy, columns); - } - - - /** - * Queries the {@link android.content.ContentResolver} with the specified queryUri. It will generate - * the correct query and return a list of {@link TableClass} - * - * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) - * @param queryUri The URI of the query - * @param table The table to get from. - * @param whereConditions The set of {@link Operator} to query the content provider. - * @param orderBy The order by clause without the ORDER BY - * @param columns The list of columns to query. - * @return A list of {@link TableClass} - */ - public static List queryList(ContentResolver contentResolver, Uri queryUri, - Class table, - OperatorGroup whereConditions, - String orderBy, String... columns) { - FlowCursor cursor = FlowCursor.from(contentResolver.query(queryUri, columns, whereConditions.getQuery(), null, orderBy)); - if (cursor != null) { - return FlowManager.getModelAdapter(table) - .getListModelLoader() - .load(cursor); - } - return new ArrayList<>(); - } - - /** - * Queries the {@link android.content.ContentResolver} with the specified queryUri. It will generate - * the correct query and return a the first item from the list of {@link TableClass} - * - * @param queryUri The URI of the query - * @param table The table to get from - * @param whereConditions The set of {@link Operator} to query the content provider. - * @param orderBy The order by clause without the ORDER BY - * @param columns The list of columns to query. - * @return The first {@link TableClass} of the list query from the content provider. - */ - public static TableClass querySingle(Uri queryUri, Class table, - OperatorGroup whereConditions, - String orderBy, String... columns) { - return querySingle(FlowManager.getContext().getContentResolver(), queryUri, table, whereConditions, orderBy, columns); - } - - /** - * Queries the {@link android.content.ContentResolver} with the specified queryUri. It will generate - * the correct query and return a the first item from the list of {@link TableClass} - * - * @param contentResolver The content resolver to use (if different from {@link FlowManager#getContext()}) - * @param queryUri The URI of the query - * @param table The table to get from - * @param whereConditions The set of {@link Operator} to query the content provider. - * @param orderBy The order by clause without the ORDER BY - * @param columns The list of columns to query. - * @return The first {@link TableClass} of the list query from the content provider. - */ - public static TableClass querySingle(ContentResolver contentResolver, Uri queryUri, Class table, - OperatorGroup whereConditions, - String orderBy, String... columns) { - List list = queryList(contentResolver, queryUri, table, whereConditions, orderBy, columns); - return list.size() > 0 ? list.get(0) : null; - } - -} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ModelProvider.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ModelProvider.java deleted file mode 100644 index d1d8eae33..000000000 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/provider/ModelProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.raizlabs.android.dbflow.structure.provider; - -import android.content.ContentResolver; -import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.language.Operator; -import com.raizlabs.android.dbflow.sql.language.OperatorGroup; - -/** - * Description: A base interface for Models that are connected to providers. - */ -public interface ModelProvider { - - /** - * Queries the {@link ContentResolver} of the app based on the passed parameters and - * populates this object with the first row from the returned data. - * - * @param whereOperatorGroup The set of {@link Operator} to filter the query by. - * @param orderBy The order by without the ORDER BY - * @param columns The list of columns to select. Leave blank for * - */ - void load(@NonNull OperatorGroup whereOperatorGroup, - @Nullable String orderBy, String... columns); - - /** - * Queries the {@link ContentResolver} of the app based on the primary keys of the object and populates - * this object with the first row from the returned data. - */ - void load(); - - /** - * @return The {@link android.net.Uri} that passes to a {@link android.content.ContentProvider} to delete a Model. - */ - Uri getDeleteUri(); - - /** - * @return The {@link android.net.Uri} that passes to a {@link android.content.ContentProvider} to insert a Model. - */ - Uri getInsertUri(); - - /** - * @return The {@link android.net.Uri} that passes to a {@link android.content.ContentProvider} to update a Model. - */ - Uri getUpdateUri(); - - /** - * @return The {@link android.net.Uri} that passes to a {@link android.content.ContentProvider} to query a Model. - */ - Uri getQueryUri(); -} diff --git a/gettingstarted.md b/gettingstarted.md new file mode 100644 index 000000000..faa8a3c49 --- /dev/null +++ b/gettingstarted.md @@ -0,0 +1,175 @@ +# GettingStarted + +This section describes how Models and tables are constructed via DBFlow. first let's describe how to get a database up and running. + +## Creating a Database + +In DBFlow, creating a database is as simple as only a few lines of code. DBFlow supports any number of databases, however individual tables and other related files can only be associated with one database. + +```kotlin +@Database(version = 1) +abstract class AppDatabase : DBFlowDatabase() +``` + +```java +@Database(version = 1) +public abstract class AppDatabase extends DBFlowDatabase { +} +``` + +The name of the database by default is the class name. To change it, read [here](usage2/usage/databases.md). + +Writing this file generates \(by default\) a `AppDatabaseAppDatabase_Database.java` file, which contains tables, views, and more all tied to a specific database. This class is automatically placed into the main `GeneratedDatabaseHolder`, which holds potentially many databases. The name, `AppDatabaseAppDatabase_Database.java`, is generated via {DatabaseClassName}{DatabaseFileName}\_Database + +To learn more about what you can configure in a database, read [here](usage2/usage/databases.md) + +## Initialize FlowManager + +DBFlow needs an instance of `Context` in order to use it for a few features such as reading from assets, content observing, and generating `ContentProvider`. + +Initialize in your `Application` subclass. You can also initialize it from other `Context` but we always grab the `Application` `Context` \(this is done only once\). + +```kotlin +class ExampleApplication : Application { + + override fun onCreate() { + super.onCreate() + FlowManager.init(this) + } +} +``` + +```java +public class ExampleApplication extends Application { + + @Override + public void onCreate() { + super.onCreate(); + FlowManager.init(this); + } +} +``` + +By default without passing in a `DatabaseConfig`, we construct an `AndroidSQLiteOpenHelper` database instance. To learn more about what you can configure in a database, read [here](usage2/usage/databases.md), including providing own database instances. + +Finally, add the custom `Application` definition to the manifest \(with the name that you chose for the class\): + +```markup + + +``` + +A database within DBFlow is only initialized once you call `database()`. If you don't want this behavior or prefer it to happen immediately, modify your `FlowConfig`: + +```kotlin +override fun onCreate() { + super.onCreate() + FlowManager.init(FlowConfig.builder(this) + .openDatabasesOnInit(true) + .build()) +} +``` + +```java +@Override +public void onCreate() { + super.onCreate(); + FlowManager.init(FlowConfig.builder(this) + .openDatabasesOnInit(true) + .build()); +} +``` + +If you do not like the built-in `DefaultTransactionManager`, or just want to roll your own existing system: + +```kotlin +FlowManager.init(FlowConfig.builder(this) + .database(DatabaseConfig.builder(AppDatabase::class) + .transactionManager(CustomTransactionManager()) + .build())) +``` + +You can define different kinds for each database. To read more on transactions and subclassing `BaseTransactionManager` go [here](usage2/usage/storingdata.md) + +## Create Models + +Creating models are as simple as defining the model class, and adding the `@Table` annotation. To read more on this, read [here](usage2/usage/models.md). + +**For now**: Models must provide a default, parameterless constructor. An example: + +```kotlin +@Table(database = TestDatabase::class) + class Currency(@PrimaryKey(autoincrement = true) var id: Long = 0, + @Column @Unique var symbol: String? = null, + @Column var shortName: String? = null, + @Column @Unique var name: String = "") // nullability of fields are respected. We will not assign a null value to this field. +``` + +or with Java: + +```java +@Table(database = TestDatabase.class) +public class Currency { + + @PrimaryKey(autoincrement = true) + long id; // package-private recommended, not required + + @Column + @Unique + String symbol; + + @Column + String shortName; + + @Column + @Unique + private String name; // private with getters and setters + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} +``` + +## Perform Some Queries + +DBFlow uses expressive builders to represent and translate to the SQLite language. + +A simple query in SQLite: + +```text +SELECT * FROM Currency WHERE symbol='$'; +``` + +DBFlow Kotlin \(by using our `dbflow-coroutines` module\): + +```kotlin +async { + database{ + val list = awaitTransact( + select from Currency::class + where (symbol eq "$")) { list } + + // use the objects here + } +} +``` + +or in Java with fluent syntax + +```java +SQLite.select(FlowManager.getDatabase(AppDatabase.class)) + .from(Currency.class) + .where(Currency_Table.symbol.eq("$")); +``` + +We support many kinds of complex and complicated queries using the builder language. To read more about this, see [the wrapper language docs](usage2/usage/sqlitewrapperlanguage.md) + +There is much more you can do in DBFlow. Read through the other docs to get a sense of the library. + diff --git a/gradle.properties b/gradle.properties index fa23c81e8..76cb67ec1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ -version=4.2.4 +version=5.0.0-alpha1 version_code=1 -group=com.raizlabs.android -bt_siteUrl=https://github.com/Raizlabs/DBFlow -bt_gitUrl=https://github.com/Raizlabs/DBFlow.git +group=com.dbflow5 +bt_siteUrl=https://github.com/agrosner/DBFlow +bt_gitUrl=https://github.com/agrosner/DBFlow.git bt_licenseName=The MIT License bt_licenseUrl=http://opensource.org/licenses/MIT bt_repo=Libraries diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4227ae5c7..956a16b52 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jul 14 11:05:04 EDT 2017 +#Mon Oct 15 17:14:01 EDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip diff --git a/ISSUE_TEMPLATE.md b/issue_template.md similarity index 73% rename from ISSUE_TEMPLATE.md rename to issue_template.md index 0f7f1d5c8..51b578b33 100644 --- a/ISSUE_TEMPLATE.md +++ b/issue_template.md @@ -1,5 +1,8 @@ +# ISSUE\_TEMPLATE + DBFlow Version: Bug or Feature Request: Description: + diff --git a/kotlin-artifacts.gradle b/kotlin-artifacts.gradle index b4f2d1740..38f9f6cb5 100644 --- a/kotlin-artifacts.gradle +++ b/kotlin-artifacts.gradle @@ -23,18 +23,11 @@ install { } } -// restore when found. -/*task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource -}*/ - -/*task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -}*/ +task sourcesJar(type: Jar) { + from "src" + classifier = "sources" +} artifacts { - //archives sourcesJar - //archives javadocJar + archives sourcesJar } \ No newline at end of file diff --git a/lib/build.gradle b/lib/build.gradle new file mode 100644 index 000000000..e476417d4 --- /dev/null +++ b/lib/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +project.ext.artifactId = bt_name + +android { + compileSdkVersion target_sdk + + defaultConfig { + minSdkVersion min_sdk + targetSdkVersion target_sdk + } + + lintOptions { + abortOnError false + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } +} + +dependencies { + api project(':core') + api deps.support_annotations + api "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" +} + +apply from: '../kotlin-artifacts.gradle' + diff --git a/dbflow/gradle.properties b/lib/gradle.properties similarity index 100% rename from dbflow/gradle.properties rename to lib/gradle.properties diff --git a/dbflow/settings.gradle b/lib/settings.gradle similarity index 100% rename from dbflow/settings.gradle rename to lib/settings.gradle diff --git a/lib/src/main/AndroidManifest.xml b/lib/src/main/AndroidManifest.xml new file mode 100644 index 000000000..1c6093f3a --- /dev/null +++ b/lib/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/lib/src/main/kotlin/com/dbflow5/SqlUtils.kt b/lib/src/main/kotlin/com/dbflow5/SqlUtils.kt new file mode 100644 index 000000000..9fd668c79 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/SqlUtils.kt @@ -0,0 +1,206 @@ +@file:JvmName("SqlUtils") + +package com.dbflow5 + +import android.content.ContentValues +import android.net.Uri +import com.dbflow5.isNotNullOrEmpty +import com.dbflow5.quoteIfNeeded +import com.dbflow5.stripQuotes +import com.dbflow5.config.FlowManager +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.NameAlias +import com.dbflow5.query.Operator +import com.dbflow5.query.OperatorGroup +import com.dbflow5.query.SQLOperator +import com.dbflow5.structure.ChangeAction +import com.dbflow5.structure.Model + +/** + * Description: Provides some handy methods for dealing with SQL statements. It's purpose is to move the + * methods away from the [Model] class and let any class use these. + */ +private val hexArray = "0123456789ABCDEF".toCharArray() + +val TABLE_QUERY_PARAM = "tableName" + +/** + * Constructs a [Uri] from a set of [SQLOperator] for specific table. + * + * @param modelClass The class of table, + * @param action The action to use. + * @param conditions The set of key-value [SQLOperator] to construct into a uri. + * @return The [Uri]. + */ +fun getNotificationUri(contentAuthority: String, + modelClass: Class<*>, + action: ChangeAction?, + conditions: Iterable?): Uri { + val uriBuilder = Uri.Builder().scheme("dbflow") + .authority(contentAuthority) + .appendQueryParameter(TABLE_QUERY_PARAM, FlowManager.getTableName(modelClass)) + if (action != null) { + uriBuilder.fragment(action.name) + } + if (conditions != null) { + for (condition in conditions) { + uriBuilder.appendQueryParameter(Uri.encode(condition.columnName()), Uri.encode(condition.value().toString())) + } + } + return uriBuilder.build() +} + + +/** + * Constructs a [Uri] from a set of [SQLOperator] for specific table. + * + * @param modelClass The class of table, + * @param action The action to use. + * @param conditions The set of key-value [SQLOperator] to construct into a uri. + * @return The [Uri]. + */ +fun getNotificationUri(contentAuthority: String, + modelClass: Class<*>, + action: ChangeAction?, + conditions: Array?): Uri { + val uriBuilder = Uri.Builder().scheme("dbflow") + .authority(contentAuthority) + .appendQueryParameter(TABLE_QUERY_PARAM, FlowManager.getTableName(modelClass)) + action?.let { uriBuilder.fragment(action.name) } + if (conditions != null && conditions.isNotEmpty()) { + for (condition in conditions) { + uriBuilder.appendQueryParameter(Uri.encode(condition.columnName()), + Uri.encode(condition.value().toString())) + } + } + return uriBuilder.build() +} + +/** + * Returns the uri for notifications from model changes + * + * @param modelClass The class to get table from. + * @param action the action changed. + * @param notifyKey The column key. + * @param notifyValue The column value that gets turned into a String. + * @return Notification uri. + */ +@JvmOverloads +fun getNotificationUri(contentAuthority: String, + modelClass: Class<*>, + action: ChangeAction?, + notifyKey: String = "", + notifyValue: Any? = null): Uri { + var operator: Operator? = null + if (notifyKey.isNotNullOrEmpty()) { + operator = Operator.op(NameAlias.Builder(notifyKey).build()).value(notifyValue) + } + return getNotificationUri(contentAuthority, modelClass, action, + if (operator != null) arrayOf(operator) else null) +} + + +/** + * Drops an active TRIGGER by specifying the onTable and triggerName + * + * @param mOnTable The table that this trigger runs on + * @param triggerName The name of the trigger + */ +fun dropTrigger(databaseWrapper: DatabaseWrapper, triggerName: String) { + databaseWrapper.execSQL("DROP TRIGGER IF EXISTS " + triggerName) +} + +/** + * Drops an active INDEX by specifying the onTable and indexName + * + * @param indexName The name of the index. + */ +fun dropIndex(databaseWrapper: DatabaseWrapper, indexName: String) { + databaseWrapper.execSQL("DROP INDEX IF EXISTS ${indexName.quoteIfNeeded()}") +} + +/** + * Adds [ContentValues] to the specified [OperatorGroup]. + * + * @param contentValues The content values to convert. + * @param operatorGroup The group to put them into as [Operator]. + */ +fun addContentValues(contentValues: ContentValues, operatorGroup: OperatorGroup) { + val entries = contentValues.valueSet() + + for ((key) in entries) { + operatorGroup.and(Operator.op(NameAlias.Builder(key).build()) + .`is`(contentValues.get(key))) + } +} + +/** + * @param contentValues The object to check existence of. + * @param key The key to check. + * @return The key, whether it's quoted or not. + */ +fun getContentValuesKey(contentValues: ContentValues, key: String): String { + val quoted = key.quoteIfNeeded() + return when { + contentValues.containsKey(quoted) -> quoted ?: "" + else -> { + val stripped = key.stripQuotes() + when { + contentValues.containsKey(stripped) -> stripped ?: "" + else -> throw IllegalArgumentException("Could not find the specified key in the Content Values object.") + } + } + } +} + +fun longForQuery(wrapper: DatabaseWrapper, query: String): Long = + wrapper.compileStatement(query).let { statement -> + try { + statement.simpleQueryForLong() + } finally { + statement.close() + } + } + +fun doubleForQuery(wrapper: DatabaseWrapper, query: String): Double = + wrapper.compileStatement(query).let { statement -> + try { + statement.simpleQueryForLong().toDouble() + } finally { + statement.close() + } + } + +/** + * Converts a byte[] to a String hex representation for within wrapper queries. + */ +fun byteArrayToHexString(bytes: ByteArray?): String { + if (bytes == null) return "" + val hexChars = CharArray(bytes.size * 2) + for (j in bytes.indices) { + val v = bytes[j].toInt() and 0xFF + hexChars[j * 2] = hexArray[v.ushr(4)] + hexChars[j * 2 + 1] = hexArray[v and 0x0F] + } + return String(hexChars) +} + +fun sqlEscapeString(value: String): String = buildString { appendEscapedSQLString(this, value) } + +fun appendEscapedSQLString(sb: StringBuilder, sqlString: String) { + sb.apply { + append('\'') + if (sqlString.indexOf('\'') != -1) { + val length = sqlString.length + for (i in 0 until length) { + val c = sqlString[i] + if (c == '\'') { + append('\'') + } + append(c) + } + } else + append(sqlString) + append('\'') + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/CacheAdapter.kt b/lib/src/main/kotlin/com/dbflow5/adapter/CacheAdapter.kt new file mode 100644 index 000000000..53ea4a8fd --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/CacheAdapter.kt @@ -0,0 +1,96 @@ +package com.dbflow5.adapter + +import com.dbflow5.annotation.DEFAULT_CACHE_SIZE +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.cache.IMultiKeyCacheConverter +import com.dbflow5.query.cache.ModelCache +import com.dbflow5.query.cache.SimpleMapCache +import com.dbflow5.structure.InvalidDBConfiguration + +/** + * Description: + */ +abstract class CacheAdapter { + + val modelCache: ModelCache by lazy { createModelCache() } + + open val cacheSize: Int + get() = DEFAULT_CACHE_SIZE + + open val cachingColumnSize: Int + get() = 1 + + open val cacheConverter: IMultiKeyCacheConverter<*> + get() = throw InvalidDBConfiguration("For multiple primary keys, a public static IMultiKeyCacheConverter field must" + + "be marked with @MultiCacheField in the corresponding model class. The resulting key" + + "must be a unique combination of the multiple keys, otherwise inconsistencies may occur.") + + /** + * @param cursor The cursor to load caching id from. + * @return The single cache column from cursor (if single). + */ + open fun getCachingColumnValueFromCursor(cursor: FlowCursor): Any? = Unit + + /** + * @param model The model to load cache column data from. + * @return The single cache column from model (if single). + */ + open fun getCachingColumnValueFromModel(model: T): Any? = Unit + + + /** + * Loads all primary keys from the [FlowCursor] into the inValues. The size of the array must + * match all primary keys. This method gets generated when caching is enabled. + * + * @param inValues The reusable array of values to populate. + * @param cursor The cursor to load from. + * @return The populated set of values to load from cache. + */ + open fun getCachingColumnValuesFromCursor(inValues: Array, cursor: FlowCursor): Array? = null + + /** + * Loads all primary keys from the [TModel] into the inValues. The size of the array must + * match all primary keys. This method gets generated when caching is enabled. It converts the primary fields + * of the [TModel] into the array of values the caching mechanism uses. + * + * @param inValues The reusable array of values to populate. + * @param TModel The model to load from. + * @return The populated set of values to load from cache. + */ + open fun getCachingColumnValuesFromModel(inValues: Array, TModel: T): Array? = null + + fun storeModelInCache(model: T) { + modelCache.addModel(getCachingId(model), model) + } + + fun removeModelFromCache(model: T) { + getCachingId(model)?.let { modelCache.removeModel(it) } + } + + fun clearCache() { + modelCache.clear() + } + + fun getCachingId(inValues: Array?): Any? = when { + inValues?.size == 1 -> // if it exists in cache no matter the query we will use that one + inValues.getOrNull(0) + inValues != null -> cacheConverter.getCachingKey(inValues) + else -> null + } + + open fun getCachingId(model: T): Any? = + getCachingId(getCachingColumnValuesFromModel(arrayOfNulls(cachingColumnSize), model)) + + + open fun createModelCache(): ModelCache = SimpleMapCache(cacheSize) + + /** + * Reloads relationships when loading from [FlowCursor] in a model that's cacheable. By having + * relationships with cached models, the retrieval will be very fast. + * + * @param cursor The cursor to reload from. + */ + open fun reloadRelationships(model: T, cursor: FlowCursor, databaseWrapper: DatabaseWrapper) = Unit + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/InternalAdapter.kt b/lib/src/main/kotlin/com/dbflow5/adapter/InternalAdapter.kt new file mode 100644 index 000000000..63f2814cb --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/InternalAdapter.kt @@ -0,0 +1,135 @@ +package com.dbflow5.adapter + +import android.content.ContentValues +import com.dbflow5.annotation.PrimaryKey +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.database.DatabaseWrapper + +/** + * Description: Used for our internal Adapter classes such as generated [ModelAdapter]. + */ +interface InternalAdapter { + + /** + * @return The table name of this adapter. + */ + val tableName: String + + /** + * Saves the specified model to the DB. + * + * @param model The model to save/insert/update + * @param databaseWrapper The manually specified wrapper. + */ + fun save(model: TModel, databaseWrapper: DatabaseWrapper): Boolean + + /** + * Saves a [Collection] of models to the DB. + * + * @param models The [Collection] of models to save. + * @param databaseWrapper The manually specified wrapper + * @return the count of models saved or updated. + */ + fun saveAll(models: Collection, databaseWrapper: DatabaseWrapper): Long + + /** + * Inserts the specified model into the DB. + * + * @param model The model to insert. + * @param databaseWrapper The manually specified wrapper. + */ + fun insert(model: TModel, databaseWrapper: DatabaseWrapper): Long + + /** + * Inserts a [Collection] of models into the DB. + * + * @param models The [Collection] of models to save. + * @param databaseWrapper The manually specified wrapper + * @return the count inserted + */ + fun insertAll(models: Collection, databaseWrapper: DatabaseWrapper): Long + + /** + * Updates the specified model into the DB. + * + * @param model The model to update. + * @param databaseWrapper The manually specified wrapper. + */ + fun update(model: TModel, databaseWrapper: DatabaseWrapper): Boolean + + /** + * Updates a [Collection] of models in the DB. + * + * @param models The [Collection] of models to save. + * @param databaseWrapper The manually specified wrapper + * @return count of successful updates. + */ + fun updateAll(models: Collection, databaseWrapper: DatabaseWrapper): Long + + /** + * Deletes the model from the DB + * + * @param model The model to delete + * @param databaseWrapper The manually specified wrapper. + */ + fun delete(model: TModel, databaseWrapper: DatabaseWrapper): Boolean + + /** + * Updates a [Collection] of models in the DB. + * + * @param models The [Collection] of models to save. + * @param databaseWrapper The manually specified wrapper + * @return count of successful deletions. + */ + fun deleteAll(models: Collection, databaseWrapper: DatabaseWrapper): Long + + /** + * Binds to a [DatabaseStatement] for insert. + * + * @param sqLiteStatement The statement to bind to. + * @param model The model to read from. + */ + fun bindToInsertStatement(sqLiteStatement: DatabaseStatement, model: TModel) + + /** + * Binds a [TModel] to the specified db statement + * + * @param contentValues The content values to fill. + * @param model The model values to put on the contentvalues + */ + fun bindToContentValues(contentValues: ContentValues, model: TModel) + + /** + * Binds a [TModel] to the specified db statement, leaving out the [PrimaryKey.autoincrement] + * column. + * + * @param contentValues The content values to fill. + * @param model The model values to put on the content values. + */ + fun bindToInsertValues(contentValues: ContentValues, model: TModel) + + /** + * Binds values of the model to an update [DatabaseStatement]. It repeats each primary + * key binding twice to ensure proper update statements. + */ + fun bindToUpdateStatement(updateStatement: DatabaseStatement, model: TModel) + + /** + * Binds values of the model to a delete [DatabaseStatement]. + */ + fun bindToDeleteStatement(deleteStatement: DatabaseStatement, model: TModel) + + /** + * If a model has an autoincrementing primary key, then + * this method will be overridden. + * + * @param model The model object to store the key + * @param id The key to store + */ + fun updateAutoIncrement(model: TModel, id: Number) + + /** + * @return true if the [InternalAdapter] can be cached. + */ + fun cachingEnabled(): Boolean +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/ModelAdapter.kt b/lib/src/main/kotlin/com/dbflow5/adapter/ModelAdapter.kt new file mode 100644 index 000000000..6dd017afb --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/ModelAdapter.kt @@ -0,0 +1,188 @@ +package com.dbflow5.adapter + +import android.content.ContentValues +import com.dbflow5.adapter.saveable.ListModelSaver +import com.dbflow5.adapter.saveable.ModelSaver +import com.dbflow5.annotation.ConflictAction +import com.dbflow5.annotation.ForeignKey +import com.dbflow5.annotation.Table +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.property.IProperty +import com.dbflow5.query.property.Property + +/** + * Description: Used for generated classes from the combination of [Table] and [Model]. + */ +abstract class ModelAdapter(databaseDefinition: DBFlowDatabase) + : RetrievalAdapter(databaseDefinition), InternalAdapter { + + private var _modelSaver: ModelSaver? = null + + val listModelSaver: ListModelSaver by lazy { createListModelSaver() } + + /** + * @return The query used to create this table. + */ + abstract val creationQuery: String + + /** + * @return An array of column properties, in order of declaration. + */ + abstract val allColumnProperties: Array> + + /** + * @return The query used to insert a model using a [DatabaseStatement] + */ + protected abstract val insertStatementQuery: String + + protected abstract val updateStatementQuery: String + + protected abstract val deleteStatementQuery: String + + protected abstract val saveStatementQuery: String + + /** + * @return The conflict algorithm to use when updating a row in this table. + */ + open val updateOnConflictAction: ConflictAction + get() = ConflictAction.ABORT + + /** + * @return The conflict algorithm to use when inserting a row in this table. + */ + open val insertOnConflictAction: ConflictAction + get() = ConflictAction.ABORT + + init { + tableConfig?.modelSaver?.let { modelSaver -> + modelSaver.modelAdapter = this + _modelSaver = modelSaver + } + } + + /** + * @param databaseWrapper The database used to do an insert statement. + * @return a new compiled [DatabaseStatement] representing insert. Not cached, always generated. + * To bind values use [bindToInsertStatement]. + */ + fun getInsertStatement(databaseWrapper: DatabaseWrapper): DatabaseStatement = + databaseWrapper.compileStatement(insertStatementQuery) + + /** + * @param databaseWrapper The database used to do an update statement. + * @return a new compiled [DatabaseStatement] representing update. Not cached, always generated. + * To bind values use [bindToUpdateStatement]. + */ + fun getUpdateStatement(databaseWrapper: DatabaseWrapper): DatabaseStatement = + databaseWrapper.compileStatement(updateStatementQuery) + + /** + * @param databaseWrapper The database used to do a delete statement. + * @return a new compiled [DatabaseStatement] representing delete. Not cached, always generated. + * To bind values use [bindToDeleteStatement]. + */ + fun getDeleteStatement(databaseWrapper: DatabaseWrapper): DatabaseStatement = + databaseWrapper.compileStatement(deleteStatementQuery) + + /** + * @param databaseWrapper The database used to do a save (insert or replace) statement. + * @return a new compiled [DatabaseStatement] representing insert or replace. Not cached, always generated. + * To bind values use [bindToInsertStatement]. + */ + fun getSaveStatement(databaseWrapper: DatabaseWrapper): DatabaseStatement = + databaseWrapper.compileStatement(saveStatementQuery) + + override fun save(model: T, databaseWrapper: DatabaseWrapper): Boolean = + modelSaver.save(model, databaseWrapper) + + override fun saveAll(models: Collection, databaseWrapper: DatabaseWrapper): Long = + listModelSaver.saveAll(models, databaseWrapper) + + override fun insert(model: T, databaseWrapper: DatabaseWrapper): Long = + modelSaver.insert(model, databaseWrapper) + + override fun insertAll(models: Collection, databaseWrapper: DatabaseWrapper): Long = + listModelSaver.insertAll(models, databaseWrapper) + + override fun update(model: T, databaseWrapper: DatabaseWrapper): Boolean = + modelSaver.update(model, databaseWrapper) + + override fun updateAll(models: Collection, databaseWrapper: DatabaseWrapper): Long = + listModelSaver.updateAll(models, databaseWrapper) + + override fun delete(model: T, databaseWrapper: DatabaseWrapper): Boolean = + modelSaver.delete(model, databaseWrapper) + + override fun deleteAll(models: Collection, databaseWrapper: DatabaseWrapper): Long = + listModelSaver.deleteAll(models, databaseWrapper) + + override fun bindToContentValues(contentValues: ContentValues, model: T) { + bindToInsertValues(contentValues, model) + } + + override fun bindToInsertValues(contentValues: ContentValues, model: T) { + throw RuntimeException("ContentValues are no longer generated automatically. To enable it," + + " set generateContentValues = true in @Table for $table.") + } + + /** + * If a [Model] has an auto-incrementing primary key, then + * this method will be overridden. + * + * @param model The model object to store the key + * @param id The key to store + */ + override fun updateAutoIncrement(model: T, id: Number) { + + } + + /** + * Called when we want to save our [ForeignKey] objects. usually during insert + update. + * This method is overridden when [ForeignKey] specified + */ + open fun saveForeignKeys(model: T, wrapper: DatabaseWrapper) { + + } + + /** + * Called when we want to delete our [ForeignKey] objects. During deletion [.delete] + * This method is overridden when [ForeignKey] specified + */ + open fun deleteForeignKeys(model: T, wrapper: DatabaseWrapper) { + + } + + override fun cachingEnabled(): Boolean = false + + var modelSaver: ModelSaver + get() = _modelSaver + ?: createSingleModelSaver() + .apply { modelAdapter = this@ModelAdapter } + .also { _modelSaver = it } + set(value) { + this._modelSaver = value + value.modelAdapter = this + } + + protected open fun createSingleModelSaver(): ModelSaver = ModelSaver() + + protected open fun createListModelSaver(): ListModelSaver = ListModelSaver(modelSaver) + + /** + * Retrieves a property by name from the table via the corresponding generated "_Table" class. Useful + * when you want to dynamically get a property from an [ModelAdapter] and do an operation on it. + * + * @param columnName The column name of the property. + * @return The property from the corresponding Table class. + */ + abstract fun getProperty(columnName: String): Property<*> + + /** + * @return When false, this table gets generated and associated with database, however it will not immediately + * get created upon startup. This is useful for keeping around legacy tables for migrations. + */ + open fun createWithDatabase(): Boolean = true + +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/ModelViewAdapter.kt b/lib/src/main/kotlin/com/dbflow5/adapter/ModelViewAdapter.kt new file mode 100644 index 000000000..f9ec45e09 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/ModelViewAdapter.kt @@ -0,0 +1,21 @@ +package com.dbflow5.adapter + +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.database.DatabaseWrapper + +/** + * Description: The base class for a [T] adapter that defines how it interacts with the DB. + */ +abstract class ModelViewAdapter(databaseDefinition: DBFlowDatabase) + : RetrievalAdapter(databaseDefinition) { + + /** + * @return a string of the query that is used to create this model view. + */ + abstract fun getCreationQuery(wrapper: DatabaseWrapper): String + + /** + * @return The name of this view in the database + */ + abstract val viewName: String +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/QueryModelAdapter.kt b/lib/src/main/kotlin/com/dbflow5/adapter/QueryModelAdapter.kt new file mode 100644 index 000000000..fd4be26aa --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/QueryModelAdapter.kt @@ -0,0 +1,22 @@ +package com.dbflow5.adapter + +import com.dbflow5.annotation.QueryModel +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.OperatorGroup + +/** + * Description: The baseclass for adapters to [QueryModel] that defines how it interacts with the DB. The + * where query is not defined here, rather its determined by the cursor used. + */ +abstract class QueryModelAdapter(databaseDefinition: DBFlowDatabase) + : RetrievalAdapter(databaseDefinition) { + + override fun getPrimaryConditionClause(model: T): OperatorGroup { + throw UnsupportedOperationException("QueryModels cannot check for existence") + } + + override fun exists(model: T, databaseWrapper: DatabaseWrapper): Boolean { + throw UnsupportedOperationException("QueryModels cannot check for existence") + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/RetrievalAdapter.kt b/lib/src/main/kotlin/com/dbflow5/adapter/RetrievalAdapter.kt new file mode 100644 index 000000000..45d68c98b --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/RetrievalAdapter.kt @@ -0,0 +1,125 @@ +package com.dbflow5.adapter + +import com.dbflow5.adapter.queriable.ListModelLoader +import com.dbflow5.adapter.queriable.SingleModelLoader +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.config.FlowManager +import com.dbflow5.config.TableConfig +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.OperatorGroup +import com.dbflow5.query.select + +/** + * Description: Provides a base retrieval class for all [Model] backed + * adapters. + */ +abstract class RetrievalAdapter(databaseDefinition: DBFlowDatabase) { + + /** + * Overrides the default implementation and allows you to provide your own implementation. Defines + * how a single [T] is loaded. + * + * @param singleModelLoader The loader to use. + */ + private var _singleModelLoader: SingleModelLoader? = null + + var singleModelLoader: SingleModelLoader + get() = _singleModelLoader ?: createSingleModelLoader().also { _singleModelLoader = it } + set(value) { + this._singleModelLoader = value + } + /** + * @return A new [ListModelLoader], caching will override this loader instance. + */ + /** + * Overrides the default implementation and allows you to provide your own implementation. Defines + * how a list of [T] are loaded. + * + * @param listModelLoader The loader to use. + */ + private var _listModelLoader: ListModelLoader? = null + + var listModelLoader: ListModelLoader + get() = _listModelLoader ?: createListModelLoader().also { _listModelLoader = it } + set(value) { + this._listModelLoader = value + } + + protected var tableConfig: TableConfig? = null + private set + + /** + * @return the model class this adapter corresponds to + */ + abstract val table: Class + + /** + * @return A new instance of a [SingleModelLoader]. Subsequent calls do not cache + * this object so it's recommended only calling this in bulk if possible. + */ + val nonCacheableSingleModelLoader: SingleModelLoader + get() = SingleModelLoader(table) + + /** + * @return A new instance of a [ListModelLoader]. Subsequent calls do not cache + * this object so it's recommended only calling this in bulk if possible. + */ + val nonCacheableListModelLoader: ListModelLoader + get() = ListModelLoader(table) + + init { + val databaseConfig = FlowManager.getConfig() + .getConfigForDatabase(databaseDefinition.associatedDatabaseClassFile) + if (databaseConfig != null) { + tableConfig = databaseConfig.getTableConfigForTable(table) + if (tableConfig != null) { + tableConfig?.singleModelLoader?.let { _singleModelLoader = it } + tableConfig?.listModelLoader?.let { _listModelLoader = it } + } + } + } + + /** + * Returns a new [model] based on the object passed in. Will not overwrite existing object. + */ + open fun load(model: T, databaseWrapper: DatabaseWrapper): T? = + nonCacheableSingleModelLoader.load(databaseWrapper, + (select + from table + where getPrimaryConditionClause(model)).query) + + /** + * Converts the specified [FlowCursor] into a new [T] + * + * @param cursor The cursor to load into the model + * @param wrapper The database instance to use. + */ + abstract fun loadFromCursor(cursor: FlowCursor, wrapper: DatabaseWrapper): T + + /** + * @param model The model to query values from + * @return True if it exists as a row in the corresponding database table + */ + abstract fun exists(model: T, databaseWrapper: DatabaseWrapper): Boolean + + /** + * @param model The primary condition clause. + * @return The clause that contains necessary primary conditions for this table. + */ + abstract fun getPrimaryConditionClause(model: T): OperatorGroup + + /** + * @return A new [ListModelLoader], caching will override this loader instance. + */ + protected open fun createListModelLoader(): ListModelLoader = ListModelLoader(table) + + /** + * @return A new [SingleModelLoader], caching will override this loader instance. + */ + protected open fun createSingleModelLoader(): SingleModelLoader = SingleModelLoader(table) + +} +/** + * Force loads the model from the DB. Even if caching is enabled it will requery the object. + */ diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/queriable/CacheableListModelLoader.kt b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/CacheableListModelLoader.kt new file mode 100644 index 000000000..39f063a33 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/CacheableListModelLoader.kt @@ -0,0 +1,47 @@ +package com.dbflow5.adapter.queriable + +import com.dbflow5.adapter.CacheAdapter +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.annotation.Table +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.cache.ModelCache +import com.dbflow5.query.cache.addOrReload + +/** + * Description: Loads a [List] of [T] with [Table.cachingEnabled] true. + */ +open class CacheableListModelLoader(modelClass: Class, + protected val cacheAdapter: CacheAdapter) + : ListModelLoader(modelClass) { + + protected val modelAdapter: ModelAdapter by lazy { + if (instanceAdapter !is ModelAdapter<*>) { + throw IllegalArgumentException("A non-Table type was used.") + } + val modelAdapter = instanceAdapter as ModelAdapter + if (!modelAdapter.cachingEnabled()) { + throw IllegalArgumentException("You cannot call this method for a table that has" + + " no caching id. Either use one Primary Key or use the MultiCacheKeyConverter") + } + return@lazy modelAdapter + } + + protected val modelCache: ModelCache by lazy { cacheAdapter.modelCache } + + override fun convertToData(cursor: FlowCursor, databaseWrapper: DatabaseWrapper): MutableList { + val data = mutableListOf() + val cacheValues = arrayOfNulls(cacheAdapter.cachingColumnSize) + // Ensure that we aren't iterating over this cursor concurrently from different threads + if (cursor.moveToFirst()) { + do { + val values = cacheAdapter.getCachingColumnValuesFromCursor(cacheValues, cursor) + val model = modelCache.addOrReload(cacheAdapter.getCachingId(values), + cacheAdapter, modelAdapter, cursor, databaseWrapper) + data.add(model) + } while (cursor.moveToNext()) + } + return data + } + +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/queriable/CacheableModelLoader.kt b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/CacheableModelLoader.kt new file mode 100644 index 000000000..f64d43ccd --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/CacheableModelLoader.kt @@ -0,0 +1,51 @@ +package com.dbflow5.adapter.queriable + +import com.dbflow5.adapter.CacheAdapter +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.annotation.Table +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.cache.ModelCache +import com.dbflow5.query.cache.addOrReload + +/** + * Description: Loads model data that is backed by a [ModelCache]. Used when [Table.cachingEnabled] + * is true. + */ +open class CacheableModelLoader(modelClass: Class, + protected val cacheAdapter: CacheAdapter) + : SingleModelLoader(modelClass) { + + protected val modelAdapter: ModelAdapter by lazy { + when (instanceAdapter) { + !is ModelAdapter<*> -> throw IllegalArgumentException("A non-Table type was used.") + else -> { + val modelAdapter = instanceAdapter as ModelAdapter + if (!modelAdapter.cachingEnabled()) { + throw IllegalArgumentException("""You cannot call this method for a table that + |has no caching id. Either use one Primary Key or + |use the MultiCacheKeyConverter""".trimMargin()) + } + return@lazy modelAdapter + } + } + } + + protected val modelCache: ModelCache by lazy { cacheAdapter.modelCache } + + /** + * Converts data by loading from cache based on its sequence of caching ids. Will reuse the passed + * [T] if it's not found in the cache and non-null. + * + * @return A model from cache. + */ + override fun convertToData(cursor: FlowCursor, moveToFirst: Boolean, + databaseWrapper: DatabaseWrapper): T? { + return if (!moveToFirst || cursor.moveToFirst()) { + val values = cacheAdapter.getCachingColumnValuesFromCursor( + arrayOfNulls(cacheAdapter.cachingColumnSize), cursor) + modelCache.addOrReload(cacheAdapter.getCachingId(values), cacheAdapter, modelAdapter, + cursor, databaseWrapper) + } else null + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/queriable/ListModelLoader.kt b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/ListModelLoader.kt new file mode 100644 index 000000000..61a57e591 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/ListModelLoader.kt @@ -0,0 +1,22 @@ +package com.dbflow5.adapter.queriable + +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor + +/** + * Description: Loads a [List] of [T]. + */ +open class ListModelLoader(modelClass: Class) + : ModelLoader>(modelClass) { + + override fun convertToData(cursor: FlowCursor, + databaseWrapper: DatabaseWrapper): MutableList { + val retData = arrayListOf() + if (cursor.moveToFirst()) { + do { + retData.add(instanceAdapter.loadFromCursor(cursor, databaseWrapper)) + } while (cursor.moveToNext()) + } + return retData + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/queriable/ModelLoader.kt b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/ModelLoader.kt new file mode 100644 index 000000000..0f66de445 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/ModelLoader.kt @@ -0,0 +1,39 @@ +package com.dbflow5.adapter.queriable + +import com.dbflow5.adapter.RetrievalAdapter +import com.dbflow5.config.FlowManager +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor + +/** + * Description: Represents how models load from DB. It will query a [DatabaseWrapper] + * and query for a [FlowCursor]. Then the cursor is used to convert itself into an object. + */ +abstract class ModelLoader(val modelClass: Class) { + + protected val instanceAdapter: RetrievalAdapter by lazy { FlowManager.getRetrievalAdapter(modelClass) } + + /** + * Loads the data from a query and returns it as a [TReturn]. + * + * @param databaseWrapper A custom database wrapper object to use. + * @param query The query to call. + * @return The data loaded from the database. + */ + open fun load(databaseWrapper: DatabaseWrapper, query: String): TReturn? = + load(databaseWrapper.rawQuery(query, null), databaseWrapper) + + open fun load(cursor: FlowCursor?, databaseWrapper: DatabaseWrapper): TReturn? { + var data: TReturn? = null + cursor?.use { data = convertToData(it, databaseWrapper) } + return data + } + + /** + * Specify how to convert the [FlowCursor] data into a [TReturn]. Can be null. + * + * @param cursor The cursor resulting from a query passed into [.load] + * @return A new (or reused) instance that represents the [FlowCursor]. + */ + abstract fun convertToData(cursor: FlowCursor, databaseWrapper: DatabaseWrapper): TReturn? +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleKeyCacheableListModelLoader.kt b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleKeyCacheableListModelLoader.kt new file mode 100644 index 000000000..e77016b6f --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleKeyCacheableListModelLoader.kt @@ -0,0 +1,29 @@ +package com.dbflow5.adapter.queriable + +import com.dbflow5.adapter.CacheAdapter +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.cache.addOrReload + +/** + * Description: + */ +class SingleKeyCacheableListModelLoader(tModelClass: Class, + cacheAdapter: CacheAdapter) + : CacheableListModelLoader(tModelClass, cacheAdapter) { + + override fun convertToData(cursor: FlowCursor, databaseWrapper: DatabaseWrapper): MutableList { + val data = arrayListOf() + var cacheValue: Any? + // Ensure that we aren't iterating over this cursor concurrently from different threads + if (cursor.moveToFirst()) { + do { + cacheValue = cacheAdapter.getCachingColumnValueFromCursor(cursor) + val model = modelCache.addOrReload(cacheValue, cacheAdapter, modelAdapter, cursor, databaseWrapper) + data.add(model) + } while (cursor.moveToNext()) + } + return data + } + +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleKeyCacheableModelLoader.kt b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleKeyCacheableModelLoader.kt new file mode 100644 index 000000000..c4c45fe02 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleKeyCacheableModelLoader.kt @@ -0,0 +1,29 @@ +package com.dbflow5.adapter.queriable + +import com.dbflow5.adapter.CacheAdapter +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.cache.addOrReload +import com.dbflow5.structure.Model + +/** + * Description: More optimized version of [CacheableModelLoader] which assumes that the [Model] + * only utilizes a single primary key. + */ +class SingleKeyCacheableModelLoader(modelClass: Class, + cacheAdapter: CacheAdapter) + : CacheableModelLoader(modelClass, cacheAdapter) { + + /** + * Converts data by loading from cache based on its sequence of caching ids. Will reuse the passed + * [T] if it's not found in the cache and non-null. + * + * @return A model from cache. + */ + override fun convertToData(cursor: FlowCursor, moveToFirst: Boolean, databaseWrapper: DatabaseWrapper): T? { + return if (!moveToFirst || cursor.moveToFirst()) { + val value = cacheAdapter.getCachingColumnValueFromCursor(cursor) + modelCache.addOrReload(value, cacheAdapter, modelAdapter, cursor, databaseWrapper) + } else null + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleModelLoader.kt b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleModelLoader.kt new file mode 100644 index 000000000..867f81b32 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/queriable/SingleModelLoader.kt @@ -0,0 +1,22 @@ +package com.dbflow5.adapter.queriable + +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor + +/** + * Description: Responsible for loading data into a single object. + */ +open class SingleModelLoader(modelClass: Class) + : ModelLoader(modelClass) { + + open fun convertToData(cursor: FlowCursor, + moveToFirst: Boolean, + databaseWrapper: DatabaseWrapper): T? = + if (!moveToFirst || cursor.moveToFirst()) { + instanceAdapter.loadFromCursor(cursor, databaseWrapper) + } else null + + override fun convertToData(cursor: FlowCursor, databaseWrapper: DatabaseWrapper): T? { + return convertToData(cursor, true, databaseWrapper) + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/saveable/CacheableListModelSaver.kt b/lib/src/main/kotlin/com/dbflow5/adapter/saveable/CacheableListModelSaver.kt new file mode 100644 index 000000000..10f3a8b1f --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/saveable/CacheableListModelSaver.kt @@ -0,0 +1,74 @@ +package com.dbflow5.adapter.saveable + +import com.dbflow5.adapter.CacheAdapter +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.database.DatabaseWrapper + +/** + * Description: Used for model caching, enables caching models when saving in list. + */ +class CacheableListModelSaver(modelSaver: ModelSaver, + private val cacheAdapter: CacheAdapter) + : ListModelSaver(modelSaver) { + + @Synchronized + override fun saveAll(tableCollection: Collection, + wrapper: DatabaseWrapper): Long { + return applyAndCount( + tableCollection = tableCollection, + databaseStatement = modelAdapter.getSaveStatement(wrapper), + cacheFn = cacheAdapter::storeModelInCache) { model, statement -> + modelSaver.save(model, statement, wrapper) + } + } + + @Synchronized + override fun insertAll(tableCollection: Collection, + wrapper: DatabaseWrapper): Long { + return applyAndCount( + tableCollection = tableCollection, + databaseStatement = modelAdapter.getInsertStatement(wrapper), + cacheFn = cacheAdapter::storeModelInCache) { model, statement -> + modelSaver.insert(model, statement, wrapper) > 0 + } + } + + @Synchronized + override fun updateAll(tableCollection: Collection, + wrapper: DatabaseWrapper): Long { + return applyAndCount(tableCollection, modelAdapter.getUpdateStatement(wrapper), + cacheFn = cacheAdapter::storeModelInCache) { model, statement -> + modelSaver.update(model, statement, wrapper) + } + } + + @Synchronized + override fun deleteAll(tableCollection: Collection, + wrapper: DatabaseWrapper): Long { + return applyAndCount(tableCollection, modelAdapter.getDeleteStatement(wrapper), + cacheFn = cacheAdapter::removeModelFromCache) { model, statement -> + modelSaver.delete(model, statement, wrapper) + } + } + + private inline fun applyAndCount(tableCollection: Collection, + databaseStatement: DatabaseStatement, + crossinline cacheFn: (T) -> Unit, + crossinline fn: (T, DatabaseStatement) -> Boolean): Long { + // skip if empty. + if (tableCollection.isEmpty()) { + return 0L + } + + var count = 0L + databaseStatement.use { statement -> + tableCollection.forEach { model -> + if (fn(model, statement)) { + cacheFn(model) + count++ + } + } + } + return count + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/saveable/ListModelSaver.kt b/lib/src/main/kotlin/com/dbflow5/adapter/saveable/ListModelSaver.kt new file mode 100644 index 000000000..6ffb88e95 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/saveable/ListModelSaver.kt @@ -0,0 +1,60 @@ +package com.dbflow5.adapter.saveable + +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.database.DatabaseWrapper + +open class ListModelSaver(val modelSaver: ModelSaver) { + + val modelAdapter = modelSaver.modelAdapter + + @Synchronized + open fun saveAll(tableCollection: Collection, + wrapper: DatabaseWrapper): Long { + return applyAndCount(tableCollection, modelAdapter.getSaveStatement(wrapper)) { model, statement -> + modelSaver.save(model, statement, wrapper) + } + } + + @Synchronized + open fun insertAll(tableCollection: Collection, + wrapper: DatabaseWrapper): Long { + return applyAndCount(tableCollection, modelAdapter.getInsertStatement(wrapper)) { model, statement -> + modelSaver.insert(model, statement, wrapper) > ModelSaver.INSERT_FAILED + } + } + + @Synchronized + open fun updateAll(tableCollection: Collection, + wrapper: DatabaseWrapper): Long { + return applyAndCount(tableCollection, modelAdapter.getUpdateStatement(wrapper)) { model, statement -> + modelSaver.update(model, statement, wrapper) + } + } + + @Synchronized + open fun deleteAll(tableCollection: Collection, + wrapper: DatabaseWrapper): Long { + return applyAndCount(tableCollection, modelAdapter.getDeleteStatement(wrapper)) { model, statement -> + modelSaver.delete(model, statement, wrapper) + } + } + + private inline fun applyAndCount(tableCollection: Collection, + databaseStatement: DatabaseStatement, + crossinline fn: (T, DatabaseStatement) -> Boolean): Long { + // skip if empty. + if (tableCollection.isEmpty()) { + return 0L + } + + var count = 0L + databaseStatement.use { statement -> + tableCollection.forEach { + if (fn(it, statement)) { + count++ + } + } + } + return count + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/adapter/saveable/ModelSaver.kt b/lib/src/main/kotlin/com/dbflow5/adapter/saveable/ModelSaver.kt new file mode 100644 index 000000000..502ed533c --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/adapter/saveable/ModelSaver.kt @@ -0,0 +1,101 @@ +package com.dbflow5.adapter.saveable + +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.runtime.NotifyDistributor +import com.dbflow5.structure.ChangeAction + +/** + * Description: Defines how models get saved into the DB. It will bind values to [DatabaseStatement] + * for all CRUD operations as they are wildly faster and more efficient than ContentValues. + */ +open class ModelSaver { + + lateinit var modelAdapter: ModelAdapter + + @Synchronized + fun save(model: T, wrapper: DatabaseWrapper): Boolean { + val insertStatement = modelAdapter.getSaveStatement(wrapper) + return insertStatement.use { save(model, insertStatement, wrapper) } + } + + @Synchronized + fun save(model: T, + insertStatement: DatabaseStatement, + wrapper: DatabaseWrapper): Boolean { + modelAdapter.saveForeignKeys(model, wrapper) + modelAdapter.bindToInsertStatement(insertStatement, model) + val id = insertStatement.executeInsert() + val success = id > INSERT_FAILED + if (success) { + modelAdapter.updateAutoIncrement(model, id) + NotifyDistributor().notifyModelChanged(model, modelAdapter, ChangeAction.CHANGE) + } + return success + } + + @Synchronized + fun update(model: T, wrapper: DatabaseWrapper): Boolean { + val updateStatement = modelAdapter.getUpdateStatement(wrapper) + return updateStatement.use { update(model, it, wrapper) } + } + + @Synchronized + fun update(model: T, databaseStatement: DatabaseStatement, wrapper: DatabaseWrapper): Boolean { + modelAdapter.saveForeignKeys(model, wrapper) + modelAdapter.bindToUpdateStatement(databaseStatement, model) + val successful = databaseStatement.executeUpdateDelete() != 0L + if (successful) { + NotifyDistributor().notifyModelChanged(model, modelAdapter, ChangeAction.UPDATE) + } + return successful + } + + @Synchronized + open fun insert(model: T, wrapper: DatabaseWrapper): Long { + val insertStatement = modelAdapter.getInsertStatement(wrapper) + return insertStatement.use { insert(model, it, wrapper) } + } + + @Synchronized + open fun insert(model: T, + insertStatement: DatabaseStatement, + wrapper: DatabaseWrapper): Long { + modelAdapter.saveForeignKeys(model, wrapper) + modelAdapter.bindToInsertStatement(insertStatement, model) + val id = insertStatement.executeInsert() + if (id > INSERT_FAILED) { + modelAdapter.updateAutoIncrement(model, id) + NotifyDistributor().notifyModelChanged(model, modelAdapter, ChangeAction.INSERT) + } + return id + } + + @Synchronized + fun delete(model: T, wrapper: DatabaseWrapper): Boolean { + val deleteStatement = modelAdapter.getDeleteStatement(wrapper) + return deleteStatement.use { delete(model, it, wrapper) } + } + + @Synchronized + fun delete(model: T, + deleteStatement: DatabaseStatement, + wrapper: DatabaseWrapper): Boolean { + modelAdapter.deleteForeignKeys(model, wrapper) + modelAdapter.bindToDeleteStatement(deleteStatement, model) + + val success = deleteStatement.executeUpdateDelete() != 0L + if (success) { + NotifyDistributor().notifyModelChanged(model, modelAdapter, ChangeAction.DELETE) + } + modelAdapter.updateAutoIncrement(model, 0) + return success + } + + companion object { + + const val INSERT_FAILED = -1 + } +} + diff --git a/lib/src/main/kotlin/com/dbflow5/config/DBFlowDatabase.kt b/lib/src/main/kotlin/com/dbflow5/config/DBFlowDatabase.kt new file mode 100644 index 000000000..182ef2b72 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/config/DBFlowDatabase.kt @@ -0,0 +1,416 @@ +package com.dbflow5.config + +import android.content.ContentValues +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.adapter.ModelViewAdapter +import com.dbflow5.adapter.QueryModelAdapter +import com.dbflow5.adapter.queriable.ListModelLoader +import com.dbflow5.adapter.queriable.SingleModelLoader +import com.dbflow5.adapter.saveable.ModelSaver +import com.dbflow5.annotation.Database +import com.dbflow5.annotation.QueryModel +import com.dbflow5.annotation.Table +import com.dbflow5.database.AndroidSQLiteOpenHelper +import com.dbflow5.database.DatabaseCallback +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.database.OpenHelper +import com.dbflow5.migration.Migration +import com.dbflow5.runtime.DirectModelNotifier +import com.dbflow5.runtime.ModelNotifier +import com.dbflow5.structure.BaseModelView +import com.dbflow5.transaction.BaseTransactionManager +import com.dbflow5.transaction.DefaultTransactionManager +import com.dbflow5.transaction.DefaultTransactionQueue +import com.dbflow5.transaction.ITransaction +import com.dbflow5.transaction.Transaction + +/** + * Description: The main interface that all Database implementations extend from. Use this to + * pass in for operations and [Transaction]. + */ +abstract class DBFlowDatabase : DatabaseWrapper { + + private val migrationMap = hashMapOf>() + + private val modelAdapters = hashMapOf, ModelAdapter<*>>() + + private val modelTableNames = hashMapOf>() + + private val modelViewAdapterMap = linkedMapOf, ModelViewAdapter<*>>() + + private val queryModelAdapterMap = linkedMapOf, QueryModelAdapter<*>>() + + /** + * The helper that manages database changes and initialization + */ + private var _openHelper: OpenHelper? = null + + /** + * Allows for the app to listen for database changes. + */ + private var callback: DatabaseCallback? = null + + /** + * Used when resetting the DB + */ + private var isResetting = false + + lateinit var transactionManager: BaseTransactionManager + private set + + private var databaseConfig: DatabaseConfig? = null + + private var modelNotifier: ModelNotifier? = null + + /** + * @return a list of all model classes in this database. + */ + val modelClasses: List> + get() = modelAdapters.keys.toList() + + /** + * @return the [BaseModelView] list for this database. + */ + val modelViews: List> + get() = modelViewAdapterMap.keys.toList() + + /** + * @return The list of [ModelViewAdapter]. Internal method for + * creating model views in the DB. + */ + val modelViewAdapters: List> + get() = modelViewAdapterMap.values.toList() + + /** + * @return The list of [QueryModelAdapter]. Internal method for creating query models in the DB. + */ + val modelQueryAdapters: List> + get() = queryModelAdapterMap.values.toList() + + /** + * @return The map of migrations to DB version + */ + val migrations: Map> + get() = migrationMap + + /** + * Returns true if the [openHelper] has been created. + */ + var isOpened: Boolean = false + + val openHelper: OpenHelper + @Synchronized get() { + var helper = _openHelper + if (helper == null) { + val config = FlowManager.getConfig().databaseConfigMap[associatedDatabaseClassFile] + helper = if (config?.openHelperCreator != null) { + config.openHelperCreator.invoke(this, callback) + } else { + AndroidSQLiteOpenHelper(FlowManager.context, this, callback) + } + helper.performRestoreFromBackup() + isOpened = true + } + _openHelper = helper + return helper + } + + val writableDatabase: DatabaseWrapper + get() = openHelper.database + + /** + * @return The name of this database as defined in [Database] + */ + val databaseName: String + get() = databaseConfig?.databaseName ?: associatedDatabaseClassFile.simpleName + + /** + * @return The file name that this database points to + */ + val databaseFileName: String + get() = databaseName + databaseExtensionName + + /** + * @return the extension for the file name. + */ + val databaseExtensionName: String + get() = databaseConfig?.databaseExtensionName ?: ".db" + + /** + * @return True if the database will reside in memory. + */ + val isInMemory: Boolean + get() = databaseConfig?.isInMemory ?: false + + /** + * @return The version of the database currently. + */ + abstract val databaseVersion: Int + + /** + * @return True if the [Database.foreignKeyConstraintsEnforced] annotation is true. + */ + abstract val isForeignKeysSupported: Boolean + + /** + * @return The class that defines the [Database] annotation. + */ + abstract val associatedDatabaseClassFile: Class<*> + + /** + * @return True if the database is ok. If backups are enabled, we restore from backup and will + * override the return value if it replaces the main DB. + */ + val isDatabaseIntegrityOk: Boolean + get() = openHelper.isDatabaseIntegrityOk + + init { + @Suppress("LeakingThis") + applyDatabaseConfig(FlowManager.getConfig().databaseConfigMap[associatedDatabaseClassFile]) + } + + /** + * Applies a database configuration object to this class. + */ + @Suppress("UNCHECKED_CAST") + private fun applyDatabaseConfig(databaseConfig: DatabaseConfig?) { + this.databaseConfig = databaseConfig + if (databaseConfig != null) { + // initialize configuration if exists. + val tableConfigCollection = databaseConfig.tableConfigMap.values + for (tableConfig in tableConfigCollection) { + val modelAdapter: ModelAdapter = modelAdapters[tableConfig.tableClass] as ModelAdapter? + ?: continue + tableConfig.listModelLoader?.let { loader -> modelAdapter.listModelLoader = loader as ListModelLoader } + tableConfig.singleModelLoader?.let { loader -> modelAdapter.singleModelLoader = loader as SingleModelLoader } + tableConfig.modelSaver?.let { saver -> modelAdapter.modelSaver = saver as ModelSaver } + } + callback = databaseConfig.callback + } + transactionManager = if (databaseConfig?.transactionManagerCreator == null) { + DefaultTransactionManager(this) + } else { + databaseConfig.transactionManagerCreator.invoke(this) + } + } + + protected fun addModelAdapter(modelAdapter: ModelAdapter, holder: DatabaseHolder) { + holder.putDatabaseForTable(modelAdapter.table, this) + modelTableNames[modelAdapter.tableName] = modelAdapter.table + modelAdapters[modelAdapter.table] = modelAdapter + } + + protected fun addModelViewAdapter(modelViewAdapter: ModelViewAdapter, holder: DatabaseHolder) { + holder.putDatabaseForTable(modelViewAdapter.table, this) + modelViewAdapterMap[modelViewAdapter.table] = modelViewAdapter + } + + protected fun addQueryModelAdapter(queryModelAdapter: QueryModelAdapter, holder: DatabaseHolder) { + holder.putDatabaseForTable(queryModelAdapter.table, this) + queryModelAdapterMap[queryModelAdapter.table] = queryModelAdapter + } + + protected fun addMigration(version: Int, migration: Migration) { + val list = migrationMap.getOrPut(version) { arrayListOf() } + list += migration + } + + /** + * Internal method used to create the database schema. + * + * @return List of Model Adapters + */ + fun getModelAdapters(): List> = modelAdapters.values.toList() + + /** + * Returns the associated [ModelAdapter] within this database for + * the specified table. If the Model is missing the [Table] annotation, + * this will return null. + * + * @param table The model that exists in this database. + * @return The ModelAdapter for the table. + */ + fun getModelAdapterForTable(table: Class): ModelAdapter? { + @Suppress("UNCHECKED_CAST") + return modelAdapters[table] as ModelAdapter? + } + + /** + * @param tableName The name of the table in this db. + * @return The associated [ModelAdapter] within this database for the specified table name. + * If the Model is missing the [Table] annotation, this will return null. + */ + fun getModelClassForName(tableName: String): Class<*>? = modelTableNames[tableName] + + /** + * @param table the VIEW class to retrieve the ModelViewAdapter from. + * @return the associated [ModelViewAdapter] for the specified table. + */ + @Suppress("UNCHECKED_CAST") + fun getModelViewAdapterForTable(table: Class): ModelViewAdapter? = + modelViewAdapterMap[table] as ModelViewAdapter? + + /** + * @param queryModel The [QueryModel] class + * @return The adapter that corresponds to the specified class. + */ + @Suppress("UNCHECKED_CAST") + fun getQueryModelAdapterForQueryClass(queryModel: Class): QueryModelAdapter? = + queryModelAdapterMap[queryModel] as QueryModelAdapter? + + fun getModelNotifier(): ModelNotifier { + var notifier = modelNotifier + if (notifier == null) { + val config = FlowManager.getConfig().databaseConfigMap[associatedDatabaseClassFile] + notifier = if (config?.modelNotifier == null) { + DirectModelNotifier() + } else { + config.modelNotifier + } + } + modelNotifier = notifier + return notifier + } + + fun beginTransactionAsync(transaction: ITransaction): Transaction.Builder = + Transaction.Builder(transaction, this) + + inline fun beginTransactionAsync(crossinline transaction: (DatabaseWrapper) -> R): Transaction.Builder = + beginTransactionAsync(object : ITransaction { + override fun execute(databaseWrapper: DatabaseWrapper) = transaction(databaseWrapper) + }) + + fun executeTransaction(transaction: ITransaction): R { + val database = writableDatabase + try { + database.beginTransaction() + val result = transaction.execute(database) + database.setTransactionSuccessful() + return result + } finally { + database.endTransaction() + } + } + + inline fun executeTransaction(crossinline transaction: (DatabaseWrapper) -> R) = executeTransaction(object : ITransaction { + override fun execute(databaseWrapper: DatabaseWrapper) = transaction(databaseWrapper) + }) + + /** + * @return True if the [Database.consistencyCheckEnabled] annotation is true. + */ + abstract fun areConsistencyChecksEnabled(): Boolean + + /** + * @return True if the [Database.backupEnabled] annotation is true. + */ + abstract fun backupEnabled(): Boolean + + /** + * Performs a full deletion of this database. Reopens the [AndroidSQLiteOpenHelper] as well. + * + * Reapplies the [DatabaseConfig] if we have one. + * @param databaseConfig sets a new [DatabaseConfig] on this class. + */ + @JvmOverloads + fun reset(databaseConfig: DatabaseConfig? = this.databaseConfig) { + if (!isResetting) { + destroy() + // reapply configuration before opening it. + applyDatabaseConfig(databaseConfig) + openHelper.database + } + } + + /** + * Reopens the DB with the new [DatabaseConfig] specified. + * Reapplies the [DatabaseConfig] if we have one. + * + * @param databaseConfig sets a new [DatabaseConfig] on this class. + */ + @JvmOverloads + fun reopen(databaseConfig: DatabaseConfig? = this.databaseConfig) { + if (!isResetting) { + close() + _openHelper = null + isOpened = false + applyDatabaseConfig(databaseConfig) + openHelper.database + isResetting = false + } + } + + /** + * Deletes the underlying database and destroys it. + */ + fun destroy() { + if (!isResetting) { + isResetting = true + close() + openHelper.deleteDB() + _openHelper = null + isOpened = false + isResetting = false + } + } + + /** + * Closes the DB and stops the [BaseTransactionManager] + */ + fun close() { + transactionManager.stopQueue() + if (isOpened) { + openHelper.closeDB() + isOpened = false + } + } + + /** + * Saves the database as a backup on the [DefaultTransactionQueue]. This will + * create a THIRD database to use as a backup to the backup in case somehow the overwrite fails. + * + * @throws java.lang.IllegalStateException if [Database.backupEnabled] + * or [Database.consistencyCheckEnabled] is not enabled. + */ + fun backupDatabase() { + openHelper.backupDB() + } + + override val version: Int + get() = writableDatabase.version + + override fun execSQL(query: String) = writableDatabase.execSQL(query) + + override fun beginTransaction() = writableDatabase.beginTransaction() + + override fun setTransactionSuccessful() = writableDatabase.setTransactionSuccessful() + + override fun endTransaction() = writableDatabase.endTransaction() + + override fun compileStatement(rawQuery: String): DatabaseStatement = writableDatabase.compileStatement(rawQuery) + + override fun rawQuery(query: String, selectionArgs: Array?): FlowCursor = writableDatabase.rawQuery(query, selectionArgs) + + override fun updateWithOnConflict(tableName: String, + contentValues: ContentValues, + where: String?, + whereArgs: Array?, + conflictAlgorithm: Int): Long = writableDatabase.updateWithOnConflict(tableName, contentValues, where, whereArgs, conflictAlgorithm) + + override fun insertWithOnConflict( + tableName: String, + nullColumnHack: String?, + values: ContentValues, + sqLiteDatabaseAlgorithmInt: Int): Long = writableDatabase.insertWithOnConflict(tableName, nullColumnHack, values, sqLiteDatabaseAlgorithmInt) + + override fun delete(tableName: String, whereClause: String?, whereArgs: Array?): Int = writableDatabase.delete(tableName, whereClause, whereArgs) + + override fun query(tableName: String, + columns: Array?, + selection: String?, + selectionArgs: Array?, + groupBy: String?, having: String?, + orderBy: String?): FlowCursor = writableDatabase.query(tableName, columns, selection, selectionArgs, groupBy, having, orderBy) +} diff --git a/lib/src/main/kotlin/com/dbflow5/config/DatabaseConfig.kt b/lib/src/main/kotlin/com/dbflow5/config/DatabaseConfig.kt new file mode 100644 index 000000000..e81cfe7ef --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/config/DatabaseConfig.kt @@ -0,0 +1,118 @@ +package com.dbflow5.config + +import com.dbflow5.database.DatabaseCallback +import com.dbflow5.database.OpenHelper +import com.dbflow5.isNotNullOrEmpty +import com.dbflow5.runtime.ModelNotifier +import com.dbflow5.transaction.BaseTransactionManager +import kotlin.reflect.KClass + +typealias OpenHelperCreator = (DBFlowDatabase, DatabaseCallback?) -> OpenHelper +typealias TransactionManagerCreator = (DBFlowDatabase) -> BaseTransactionManager + +/** + * Description: + */ +class DatabaseConfig( + val databaseClass: Class<*>, + val openHelperCreator: OpenHelperCreator? = null, + val transactionManagerCreator: TransactionManagerCreator? = null, + val callback: DatabaseCallback? = null, + val tableConfigMap: Map, TableConfig<*>> = mapOf(), + val modelNotifier: ModelNotifier? = null, + val isInMemory: Boolean = false, + val databaseName: String? = null, + val databaseExtensionName: String? = null) { + + internal constructor(builder: Builder) : this( + // convert java interface to kotlin function. + openHelperCreator = builder.openHelperCreator, + databaseClass = builder.databaseClass, + transactionManagerCreator = builder.transactionManagerCreator, + callback = builder.callback, + tableConfigMap = builder.tableConfigMap, + modelNotifier = builder.modelNotifier, + isInMemory = builder.inMemory, + databaseName = builder.databaseName ?: builder.databaseClass.simpleName, + databaseExtensionName = when { + builder.databaseExtensionName == null -> ".db" + builder.databaseExtensionName.isNotNullOrEmpty() -> ".${builder.databaseExtensionName}" + else -> "" + }) + + @Suppress("UNCHECKED_CAST") + fun getTableConfigForTable(modelClass: Class): TableConfig? = + tableConfigMap[modelClass] as TableConfig? + + /** + * Build compatibility class for Java. Use the [DatabaseConfig] class directly if Kotlin consumer. + */ + class Builder(internal val databaseClass: Class<*>, + internal val openHelperCreator: OpenHelperCreator? = null) { + + internal var transactionManagerCreator: TransactionManagerCreator? = null + internal var callback: DatabaseCallback? = null + internal val tableConfigMap: MutableMap, TableConfig<*>> = hashMapOf() + internal var modelNotifier: ModelNotifier? = null + internal var inMemory = false + internal var databaseName: String? = null + internal var databaseExtensionName: String? = null + + constructor(kClass: KClass<*>, openHelperCreator: OpenHelperCreator) + : this(kClass.java, openHelperCreator) + + fun transactionManagerCreator(creator: TransactionManagerCreator) = apply { + this.transactionManagerCreator = creator + } + + fun helperListener(callback: DatabaseCallback) = apply { + this.callback = callback + } + + fun addTableConfig(tableConfig: TableConfig<*>) = apply { + tableConfigMap.put(tableConfig.tableClass, tableConfig) + } + + fun modelNotifier(modelNotifier: ModelNotifier) = apply { + this.modelNotifier = modelNotifier + } + + fun inMemory() = apply { + inMemory = true + } + + /** + * @return Pass in dynamic database name here. Otherwise it defaults to class name. + */ + fun databaseName(name: String) = apply { + databaseName = name + } + + /** + * @return Pass in the extension for the DB here. + * Otherwise defaults to ".db". If empty string passed, no extension is used. + */ + fun extensionName(name: String) = apply { + databaseExtensionName = name + } + + fun build() = DatabaseConfig(this) + } + + companion object { + + @JvmStatic + fun builder(database: Class<*>, openHelperCreator: OpenHelperCreator): Builder = + Builder(database, openHelperCreator) + + fun builder(database: KClass<*>, openHelperCreator: OpenHelperCreator): Builder = + Builder(database, openHelperCreator) + + @JvmStatic + fun inMemoryBuilder(database: Class<*>, openHelperCreator: OpenHelperCreator): Builder = + Builder(database, openHelperCreator).inMemory() + + fun inMemoryBuilder(database: KClass<*>, openHelperCreator: OpenHelperCreator): Builder = + Builder(database, openHelperCreator).inMemory() + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/config/DatabaseHolder.kt b/lib/src/main/kotlin/com/dbflow5/config/DatabaseHolder.kt new file mode 100644 index 000000000..38d15f40e --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/config/DatabaseHolder.kt @@ -0,0 +1,60 @@ +package com.dbflow5.config + +import com.dbflow5.converter.TypeConverter + +/** + * Description: The base interface for interacting with all of the database and top-level data that's shared + * between them. + */ +abstract class DatabaseHolder { + + val databaseDefinitionMap: MutableMap, DBFlowDatabase> = hashMapOf() + val databaseNameMap: MutableMap = hashMapOf() + val databaseClassLookupMap: MutableMap, DBFlowDatabase> = hashMapOf() + + @JvmField + val typeConverters: MutableMap, TypeConverter<*, *>> = hashMapOf() + + val databaseDefinitions: List + get() = databaseNameMap.values.toList() + + /** + * @param clazz The model value class to get a [TypeConverter] + * @return Type converter for the specified model value. + */ + fun getTypeConverterForClass(clazz: Class<*>): TypeConverter<*, *>? = typeConverters[clazz] + + /** + * @param table The model class + * @return The database that the table belongs in + */ + fun getDatabaseForTable(table: Class<*>): DBFlowDatabase? = databaseDefinitionMap[table] + + fun getDatabase(databaseClass: Class<*>): DBFlowDatabase? = + databaseClassLookupMap[databaseClass] + + /** + * @param databaseName The name of the database to retrieve + * @return The database that has the specified name + */ + fun getDatabase(databaseName: String): DBFlowDatabase? = databaseNameMap[databaseName] + + /** + * Helper method used to store a database for the specified table. + * + * @param table The model table + * @param databaseDefinition The database definition + */ + fun putDatabaseForTable(table: Class<*>, databaseDefinition: DBFlowDatabase) { + databaseDefinitionMap.put(table, databaseDefinition) + databaseNameMap.put(databaseDefinition.databaseName, databaseDefinition) + databaseClassLookupMap.put(databaseDefinition.associatedDatabaseClassFile, databaseDefinition) + } + + fun reset() { + databaseDefinitionMap.clear() + databaseNameMap.clear() + databaseClassLookupMap.clear() + typeConverters.clear() + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/config/FlowConfig.kt b/lib/src/main/kotlin/com/dbflow5/config/FlowConfig.kt new file mode 100644 index 000000000..204c285e6 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/config/FlowConfig.kt @@ -0,0 +1,67 @@ +package com.dbflow5.config + +import android.content.Context + +/** + * Description: The main configuration instance for DBFlow. This + */ +class FlowConfig(val context: Context, + val databaseHolders: Set> = setOf(), + val databaseConfigMap: Map, DatabaseConfig> = mapOf(), + val openDatabasesOnInit: Boolean = false) { + + internal constructor(builder: Builder) : this( + databaseHolders = builder.databaseHolders.toSet(), + databaseConfigMap = builder.databaseConfigMap, + context = builder.context, + openDatabasesOnInit = builder.openDatabasesOnInit + ) + + fun getConfigForDatabase(databaseClass: Class<*>): DatabaseConfig? { + return databaseConfigMap[databaseClass] + } + + /** + * Merges two [FlowConfig] together by combining an existing config. Any new specified [DatabaseConfig] + * will override existing ones. + */ + internal fun merge(flowConfig: FlowConfig): FlowConfig = FlowConfig( + context = flowConfig.context, + databaseConfigMap = databaseConfigMap.entries + .map { (key, value) -> + key to (flowConfig.databaseConfigMap[key] ?: value) + }.toMap(), + databaseHolders = databaseHolders.plus(flowConfig.databaseHolders), + openDatabasesOnInit = flowConfig.openDatabasesOnInit) + + class Builder(context: Context) { + + internal val context: Context = context.applicationContext + internal var databaseHolders: MutableSet> = hashSetOf() + internal val databaseConfigMap: MutableMap, DatabaseConfig> = hashMapOf() + internal var openDatabasesOnInit: Boolean = false + + fun addDatabaseHolder(databaseHolderClass: Class) = apply { + databaseHolders.add(databaseHolderClass) + } + + fun database(databaseConfig: DatabaseConfig) = apply { + databaseConfigMap.put(databaseConfig.databaseClass, databaseConfig) + } + + /** + * @param openDatabasesOnInit true if we want all databases open. + * @return True to open all associated databases in DBFlow on calling of [FlowManager.init] + */ + fun openDatabasesOnInit(openDatabasesOnInit: Boolean) = apply { + this.openDatabasesOnInit = openDatabasesOnInit + } + + fun build() = FlowConfig(this) + } + + companion object { + + fun builder(context: Context): Builder = Builder(context) + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/config/FlowLog.kt b/lib/src/main/kotlin/com/dbflow5/config/FlowLog.kt new file mode 100644 index 000000000..bba044ac9 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/config/FlowLog.kt @@ -0,0 +1,131 @@ +package com.dbflow5.config + +import android.os.Build +import android.util.Log +import com.dbflow5.config.FlowLog.Level + +/** + * Description: Mirrors [Log] with its own [Level] flag. + */ +object FlowLog { + + val TAG = "FlowLog" + private var level = Level.E + + /** + * Sets the minimum level that we wish to print out log statements with. + * The default is [Level.E]. + * + * @param level + */ + @JvmStatic + fun setMinimumLoggingLevel(level: Level) { + FlowLog.level = level + } + + /** + * Logs information to the [Log] class. It wraps around the standard implementation. + * + * @param level The log level to use + * @param tag The tag of the log + * @param message The message to print out + * @param throwable The optional stack trace to print + */ + @JvmOverloads + @JvmStatic + fun log(level: Level, tag: String = TAG, message: String? = "", throwable: Throwable? = null) { + if (isEnabled(level)) { + level.call(tag, message, throwable) + } + } + + /** + * Logs information to the [Log] class. It wraps around the standard implementation. + * + * @param level The log level to use + * @param tag The tag of the log + * @param message The message to print out + * @param throwable The optional stack trace to print + */ + @JvmStatic + fun log(level: Level, message: String, throwable: Throwable?) = log(level = level, tag = TAG, message = message, throwable = throwable) + + /** + * Returns true if the logging level is lower than the specified [Level] + * + * @return + */ + @JvmStatic + fun isEnabled(level: Level) = level.ordinal >= FlowLog.level.ordinal + + /** + * Logs a [Throwable] as an error. + * + * @param throwable The stack trace to print + */ + @JvmStatic + fun logError(throwable: Throwable) { + log(Level.E, throwable = throwable) + } + + /** + * Logs a [Throwable] as a warning. + * + * @param throwable The stack trace to print + */ + @JvmStatic + fun logWarning(throwable: Throwable) { + log(Level.W, throwable = throwable) + } + + /** + * Defines a log level that will execute + */ + enum class Level { + V { + override fun call(tag: String, message: String?, throwable: Throwable?) { + Log.v(tag, message, throwable) + } + }, + D { + override fun call(tag: String, message: String?, throwable: Throwable?) { + Log.d(tag, message, throwable) + } + }, + I { + override fun call(tag: String, message: String?, throwable: Throwable?) { + Log.i(tag, message, throwable) + } + }, + W { + override fun call(tag: String, message: String?, throwable: Throwable?) { + Log.w(tag, message, throwable) + } + }, + E { + override fun call(tag: String, message: String?, throwable: Throwable?) { + Log.e(tag, message, throwable) + } + }, + WTF { + override fun call(tag: String, message: String?, throwable: Throwable?) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + Log.wtf(tag, message, throwable) + } else { + // If on older platform, we will just exaggerate the log message in the error level + Log.e(tag, "!!!!!!!!*******$message********!!!!!!", throwable) + } + } + }; + + internal abstract fun call(tag: String, message: String?, throwable: Throwable?) + } + +} +/** + * Logs information to the [Log] class. It wraps around the standard implementation. + * It uses the [.TAG] for messages and sends a null throwable. + * + * @param level The log level to use + * @param message The message to print out + */ diff --git a/lib/src/main/kotlin/com/dbflow5/config/FlowManager.kt b/lib/src/main/kotlin/com/dbflow5/config/FlowManager.kt new file mode 100644 index 000000000..9d220aaf7 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/config/FlowManager.kt @@ -0,0 +1,476 @@ +@file:Suppress("NOTHING_TO_INLINE") + +package com.dbflow5.config + +import android.annotation.SuppressLint +import android.content.Context +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.adapter.ModelViewAdapter +import com.dbflow5.adapter.QueryModelAdapter +import com.dbflow5.adapter.RetrievalAdapter +import com.dbflow5.annotation.Table +import com.dbflow5.converter.TypeConverter +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.migration.Migration +import com.dbflow5.quote +import com.dbflow5.runtime.ModelNotifier +import com.dbflow5.runtime.TableNotifierRegister +import com.dbflow5.structure.BaseModel +import com.dbflow5.structure.BaseModelView +import com.dbflow5.structure.BaseQueryModel +import com.dbflow5.structure.InvalidDBConfiguration +import com.dbflow5.structure.Model +import kotlin.reflect.KClass + +/** + * Description: The main entry point into the generated database code. It uses reflection to look up + * and construct the generated database holder class used in defining the structure for all databases + * used in this application. + */ +object FlowManager { + + @SuppressLint("StaticFieldLeak") + internal var config: FlowConfig? = null + + private var globalDatabaseHolder = GlobalDatabaseHolder() + + private val loadedModules = hashSetOf>() + + private val DEFAULT_DATABASE_HOLDER_NAME = "GeneratedDatabaseHolder" + + private val DEFAULT_DATABASE_HOLDER_PACKAGE_NAME = FlowManager::class.java.`package`.name + + private val DEFAULT_DATABASE_HOLDER_CLASSNAME = + "$DEFAULT_DATABASE_HOLDER_PACKAGE_NAME.$DEFAULT_DATABASE_HOLDER_NAME" + + /** + * Will throw an exception if this class is not initialized yet in [.init] + * + * @return The shared context. + */ + @JvmStatic + val context: Context + get() = config?.context ?: + throw IllegalStateException("You must provide a valid FlowConfig instance." + + " We recommend calling init() in your application class.") + + private class GlobalDatabaseHolder : DatabaseHolder() { + + var isInitialized = false + private set + + fun add(holder: DatabaseHolder) { + databaseDefinitionMap.putAll(holder.databaseDefinitionMap) + databaseNameMap.putAll(holder.databaseNameMap) + typeConverters.putAll(holder.typeConverters) + databaseClassLookupMap.putAll(holder.databaseClassLookupMap) + isInitialized = true + } + } + + /** + * Returns the table name for the specific model class + * + * @param table The class that implements [Model] + * @return The table name, which can be different than the [Model] class name + */ + @JvmStatic + fun getTableName(table: Class<*>): String { + return getModelAdapterOrNull(table)?.tableName + ?: getModelViewAdapterOrNull(table)?.viewName + ?: throwCannotFindAdapter("ModelAdapter/ModelViewAdapter", table) + } + + /** + * @param databaseName The name of the database. Will throw an exception if the databaseForTable doesn't exist. + * @param tableName The name of the table in the DB. + * @return The associated table class for the specified name. + */ + @JvmStatic + fun getTableClassForName(databaseName: String, tableName: String): Class<*> { + val databaseDefinition = getDatabase(databaseName) + return databaseDefinition.getModelClassForName(tableName) + ?: databaseDefinition.getModelClassForName(tableName.quote()) + ?: throw IllegalArgumentException("The specified table $tableName was not found." + + " Did you forget to add the @Table annotation and point it to $databaseName?") + } + + /** + * @param databaseClass The class of the database. Will throw an exception if the databaseForTable doesn't exist. + * @param tableName The name of the table in the DB. + * @return The associated table class for the specified name. + */ + @JvmStatic + fun getTableClassForName(databaseClass: Class, tableName: String): Class<*> { + val databaseDefinition = getDatabase(databaseClass) + return databaseDefinition.getModelClassForName(tableName) + ?: databaseDefinition.getModelClassForName(tableName.quote()) + ?: throw IllegalArgumentException("The specified table $tableName was not found." + + " Did you forget to add the @Table annotation and point it to $databaseClass?") + } + + /** + * @param table The table to lookup the database for. + * @return the corresponding [DBFlowDatabase] for the specified model + */ + @JvmStatic + fun getDatabaseForTable(table: Class<*>): DBFlowDatabase { + checkDatabaseHolder() + return globalDatabaseHolder.getDatabaseForTable(table) ?: + throw InvalidDBConfiguration("Model object: ${table.name} is not registered with a Database." + + " Did you forget an annotation?") + } + + @Suppress("UNCHECKED_CAST") + @JvmStatic + fun getDatabase(databaseClass: Class): T { + checkDatabaseHolder() + return globalDatabaseHolder.getDatabase(databaseClass) as? T ?: + throw InvalidDBConfiguration("Database: ${databaseClass.name} is not a registered Database. " + + "Did you forget the @Database annotation?") + } + + @JvmStatic + fun getDatabaseName(database: Class): String = getDatabase(database).databaseName + + /** + * @param databaseName The name of the database. Will throw an exception if the databaseForTable doesn't exist. + * @return the [DBFlowDatabase] for the specified database + */ + @JvmStatic + fun getDatabase(databaseName: String): DBFlowDatabase { + checkDatabaseHolder() + return globalDatabaseHolder.getDatabase(databaseName) ?: + throw InvalidDBConfiguration("The specified database $databaseName was not found. " + + "Did you forget the @Database annotation?") + } + + @Deprecated(replaceWith = ReplaceWith("FlowManager.getDatabaseForTable(table)"), + message = "This method is no longer needed. DBFlowDatabase now delegates to the DatabaseWrapper.") + @JvmStatic + fun getWritableDatabaseForTable(table: Class<*>): DatabaseWrapper = + getDatabaseForTable(table).writableDatabase + + @Deprecated(replaceWith = ReplaceWith("FlowManager.getDatabase(databaseName)"), + message = "This method is no longer needed. DBFlowDatabase now delegates to the DatabaseWrapper.") + @JvmStatic + fun getWritableDatabase(databaseName: String): DatabaseWrapper = + getDatabase(databaseName).writableDatabase + + @Deprecated(replaceWith = ReplaceWith("FlowManager.getDatabase(databaseClass)"), + message = "This method is no longer needed. DBFlowDatabase now delegates to the DatabaseWrapper.") + @JvmStatic + fun getWritableDatabase(databaseClass: Class): DatabaseWrapper = + getDatabase(databaseClass).writableDatabase + + /** + * Loading the module Database holder via reflection. + * + * + * It is assumed FlowManager.init() is called by the application that uses the + * module database. This method should only be called if you need to load databases + * that are part of a module. Building once will give you the ability to add the class. + */ + @JvmStatic + fun initModule(generatedClassName: Class) { + loadDatabaseHolder(generatedClassName) + } + + @JvmStatic + fun getConfig(): FlowConfig = config ?: + throw IllegalStateException("Configuration is not initialized. " + + "Please call init(FlowConfig) in your application class.") + + /** + * @return The database holder, creating if necessary using reflection. + */ + @JvmStatic + internal fun loadDatabaseHolder(holderClass: Class) { + if (loadedModules.contains(holderClass)) { + return + } + + try { + // Load the database holder, and add it to the global collection. + val dbHolder = holderClass.newInstance() + if (dbHolder != null) { + globalDatabaseHolder.add(dbHolder) + + // Cache the holder for future reference. + loadedModules.add(holderClass) + } + } catch (e: Throwable) { + e.printStackTrace() + throw ModuleNotFoundException("Cannot load $holderClass", e) + } + + } + + /** + * Resets all databases and associated files. + */ + @Synchronized + @JvmStatic + fun reset() { + globalDatabaseHolder.databaseClassLookupMap.values.forEach { it.reset() } + globalDatabaseHolder.reset() + loadedModules.clear() + } + + /** + * Close all DB files and resets [FlowConfig] and the [GlobalDatabaseHolder]. Brings + * DBFlow back to initial application state. + */ + @Synchronized + @JvmStatic + fun close() { + globalDatabaseHolder.databaseClassLookupMap.values.forEach { it.close() } + config = null + globalDatabaseHolder = GlobalDatabaseHolder() + loadedModules.clear() + } + + /** + * Helper method to simplify the [.init]. Use [.init] to provide + * more customization. + * + * @param context - should be application context, but not necessary as we retrieve it anyways. + */ + @JvmStatic + fun init(context: Context) { + init(FlowConfig.Builder(context).build()) + } + + /** + * Initializes DBFlow, loading the main application Database holder via reflection one time only. + * This will trigger all creations, updates, and instantiation for each database defined. + * + * @param flowConfig The configuration instance that will help shape how DBFlow gets constructed. + */ + @JvmStatic + fun init(flowConfig: FlowConfig) { + config = config?.merge(flowConfig) ?: flowConfig + + @Suppress("UNCHECKED_CAST") + try { + val defaultHolderClass = Class.forName(DEFAULT_DATABASE_HOLDER_CLASSNAME) as Class + loadDatabaseHolder(defaultHolderClass) + } catch (e: ModuleNotFoundException) { + // Ignore this exception since it means the application does not have its + // own database. The initialization happens because the application is using + // a module that has a database. + FlowLog.log(level = FlowLog.Level.W, message = e.message) + } catch (e: ClassNotFoundException) { + // warning if a library uses DBFlow with module support but the app you're using doesn't support it. + FlowLog.log(level = FlowLog.Level.W, message = "Could not find the default GeneratedDatabaseHolder") + } + + flowConfig.databaseHolders.forEach { loadDatabaseHolder(it) } + + if (flowConfig.openDatabasesOnInit) { + globalDatabaseHolder.databaseDefinitions.forEach { + // triggers open, create, migrations. + it.writableDatabase + } + } + } + + /** + * @param objectClass A class with an associated type converter. May return null if not found. + * @return The specific [TypeConverter] for the specified class. It defines + * how the custom datatype is handled going into and out of the DB. + */ + @JvmStatic + fun getTypeConverterForClass(objectClass: Class<*>): TypeConverter<*, *>? { + checkDatabaseHolder() + return globalDatabaseHolder.getTypeConverterForClass(objectClass) + } + + // region Getters + + /** + * Release reference to context and [FlowConfig] + */ + @JvmStatic + @Synchronized + fun destroy() { + globalDatabaseHolder.databaseClassLookupMap.values.forEach { it.destroy() } + config = null + // Reset the global database holder. + globalDatabaseHolder = GlobalDatabaseHolder() + loadedModules.clear() + } + + /** + * @param modelClass The class that implements [Model] to find an adapter for. + * @return The adapter associated with the class. If its not a [ModelAdapter], + * it checks both the [ModelViewAdapter] and [QueryModelAdapter]. + */ + @JvmStatic + fun getRetrievalAdapter(modelClass: Class): RetrievalAdapter { + var retrievalAdapter: RetrievalAdapter? = getModelAdapterOrNull(modelClass) + if (retrievalAdapter == null) { + retrievalAdapter = getModelViewAdapterOrNull(modelClass) + if (retrievalAdapter == null) { + retrievalAdapter = getQueryModelAdapterOrNull(modelClass) + } + } + return retrievalAdapter ?: throwCannotFindAdapter("RetrievalAdapter", modelClass) + } + + + /** + * @param modelClass The class of the table + * @param [T] The class that implements [Model] + * @return The associated model adapter (DAO) that is generated from a [Table] class. Handles + * interactions with the database. This method is meant for internal usage only. + * We strongly prefer you use the built-in methods associated with [Model] and [BaseModel]. + */ + @JvmStatic + fun getModelAdapter(modelClass: Class): ModelAdapter = + getModelAdapterOrNull(modelClass) ?: throwCannotFindAdapter("ModelAdapter", modelClass) + + /** + * Returns the model view adapter for a SQLite VIEW. These are only created with the [com.dbflow5.annotation.ModelView] annotation. + * + * @param modelViewClass The class of the VIEW + * @param [T] The class that extends [BaseModelView] + * @return The model view adapter for the specified model view. + */ + @JvmStatic + fun getModelViewAdapter(modelViewClass: Class): ModelViewAdapter = + getModelViewAdapterOrNull(modelViewClass) ?: throwCannotFindAdapter("ModelViewAdapter", modelViewClass) + + /** + * Returns the query model adapter for an undefined cursor. These are only created with the [T] annotation. + * + * @param queryModelClass The class of the query + * @param [T] The class that extends [BaseQueryModel] + * @return The query model adapter for the specified model cursor. + */ + @JvmStatic + fun getQueryModelAdapter(queryModelClass: Class): QueryModelAdapter = + getQueryModelAdapterOrNull(queryModelClass) ?: throwCannotFindAdapter("QueryModelAdapter", queryModelClass) + + @JvmStatic + fun getModelNotifierForTable(table: Class<*>): ModelNotifier = + getDatabaseForTable(table).getModelNotifier() + + @JvmStatic + fun newRegisterForTable(table: Class<*>): TableNotifierRegister = + getModelNotifierForTable(table).newRegister() + + private fun getModelAdapterOrNull(modelClass: Class): ModelAdapter? = + getDatabaseForTable(modelClass).getModelAdapterForTable(modelClass) + + private fun getModelViewAdapterOrNull(modelClass: Class): ModelViewAdapter? = + getDatabaseForTable(modelClass).getModelViewAdapterForTable(modelClass) + + private fun getQueryModelAdapterOrNull(modelClass: Class): QueryModelAdapter? = + getDatabaseForTable(modelClass).getQueryModelAdapterForQueryClass(modelClass) + + /** + * @param databaseName The name of the database. Will throw an exception if the databaseForTable doesn't exist. + * @return The map of migrations for the specified database. + */ + @JvmStatic + internal fun getMigrations(databaseName: String): Map> = + getDatabase(databaseName).migrations + + /** + * Checks a standard database helper for integrity using quick_check(1). + * + * @param databaseName The name of the database to check. Will thrown an exception if it does not exist. + * @return true if it's integrity is OK. + */ + @JvmStatic + fun isDatabaseIntegrityOk(databaseName: String) = getDatabase(databaseName).openHelper.isDatabaseIntegrityOk + + private fun throwCannotFindAdapter(type: String, clazz: Class<*>): Nothing = + throw IllegalArgumentException("Cannot find $type for $clazz. Ensure the class is annotated with proper annotation.") + + private fun checkDatabaseHolder() { + if (!globalDatabaseHolder.isInitialized) { + throw IllegalStateException("The global databaseForTable holder is not initialized. " + + "Ensure you call FlowManager.init() before accessing the databaseForTable.") + } + } + + // endregion + + /** + * Exception thrown when a database holder cannot load the databaseForTable holder + * for a module. + */ + class ModuleNotFoundException(detailMessage: String, throwable: Throwable) + : RuntimeException(detailMessage, throwable) + +} + +/** + * Easily get access to its [DBFlowDatabase] directly. + */ +inline fun database(): T = database(T::class) + +inline fun database(kClass: KClass): T = FlowManager.getDatabase(kClass.java) + +/** + * Easily get access to its [DBFlowDatabase] directly. + */ +inline fun database(kClass: KClass, f: T.() -> R): R = database(kClass).f() + +inline fun database(f: T.() -> Unit): T + = database(T::class).apply(f) + +inline fun databaseForTable(kClass: KClass, f: DBFlowDatabase.() -> R): R + = databaseForTable(kClass).f() + +inline fun databaseForTable(kClass: KClass): DBFlowDatabase = FlowManager.getDatabaseForTable(kClass.java) + +inline fun databaseForTable(f: DBFlowDatabase.() -> Unit): DBFlowDatabase + = databaseForTable(T::class).apply(f) + +/** + * Easily get access to its [DBFlowDatabase] directly. + */ +inline fun databaseForTable(): DBFlowDatabase = databaseForTable(T::class) + +/** + * Easily get its table name. + */ +inline fun tableName(): String + = FlowManager.getTableName(T::class.java) + +/** + * Easily get its [ModelAdapter]. + */ +inline fun modelAdapter() = FlowManager.getModelAdapter(T::class.java) + +inline val KClass.modelAdapter + get() = FlowManager.getModelAdapter(this.java) + + +inline val Class.modelAdapter + get() = FlowManager.getModelAdapter(this) + +/** + * Easily get its [QueryModelAdapter]. + */ +inline fun queryModelAdapter() = FlowManager.getQueryModelAdapter(T::class.java) + +inline val KClass.queryModelAdapter + get() = FlowManager.getQueryModelAdapter(this.java) + +inline val Class.queryModelAdapter + get() = FlowManager.getQueryModelAdapter(this) + +/** + * Easily get its [ModelViewAdapter] + */ +inline fun modelViewAdapter() = FlowManager.getModelViewAdapter(T::class.java) + +inline val KClass.modelViewAdapter + get() = FlowManager.getModelViewAdapter(this.java) + +inline val Class.modelViewAdapter + get() = FlowManager.getModelViewAdapter(this) \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/NaturalOrderComparator.java b/lib/src/main/kotlin/com/dbflow5/config/NaturalOrderComparator.java similarity index 98% rename from dbflow/src/main/java/com/raizlabs/android/dbflow/config/NaturalOrderComparator.java rename to lib/src/main/kotlin/com/dbflow5/config/NaturalOrderComparator.java index cf76afd14..9dae66d4b 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/config/NaturalOrderComparator.java +++ b/lib/src/main/kotlin/com/dbflow5/config/NaturalOrderComparator.java @@ -1,4 +1,4 @@ -package com.raizlabs.android.dbflow.config; +package com.dbflow5.config; /* NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java. @@ -27,6 +27,14 @@ import java.util.Comparator; public class NaturalOrderComparator implements Comparator { + static char charAt(String s, int i) { + if (i >= s.length()) { + return 0; + } else { + return s.charAt(i); + } + } + public int compare(Object o1, Object o2) { String a = o1.toString(); String b = o2.toString(); @@ -90,14 +98,6 @@ public int compare(Object o1, Object o2) { } } - static char charAt(String s, int i) { - if (i >= s.length()) { - return 0; - } else { - return s.charAt(i); - } - } - int compareRight(String a, String b) { int bias = 0; int ia = 0; @@ -122,8 +122,9 @@ int compareRight(String a, String b) { bias = -1; } } else if (ca > cb) { - if (bias == 0) + if (bias == 0) { bias = +1; + } } else if (ca == 0 && cb == 0) { return bias; } diff --git a/lib/src/main/kotlin/com/dbflow5/config/TableConfig.kt b/lib/src/main/kotlin/com/dbflow5/config/TableConfig.kt new file mode 100644 index 000000000..451a8e7eb --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/config/TableConfig.kt @@ -0,0 +1,73 @@ +package com.dbflow5.config + +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.adapter.queriable.ListModelLoader +import com.dbflow5.adapter.queriable.SingleModelLoader +import com.dbflow5.adapter.saveable.ModelSaver +import kotlin.reflect.KClass + +/** + * Description: Represents certain table configuration options. This allows you to easily specify + * certain configuration options for a table. + */ +class TableConfig(val tableClass: Class, + val modelSaver: ModelSaver? = null, + val singleModelLoader: SingleModelLoader? = null, + val listModelLoader: ListModelLoader? = null) { + + internal constructor(builder: Builder) : this( + tableClass = builder.tableClass, + modelSaver = builder.modelAdapterModelSaver, + singleModelLoader = builder.singleModelLoader, + listModelLoader = builder.listModelLoader + ) + + /** + * Table builder for java consumers. use [TableConfig] directly if calling from Kotlin. + */ + class Builder(internal val tableClass: Class) { + internal var modelAdapterModelSaver: ModelSaver? = null + internal var singleModelLoader: SingleModelLoader? = null + internal var listModelLoader: ListModelLoader? = null + + constructor(tableClass: KClass) : this(tableClass.java) + + /** + * Define how the [ModelAdapter] saves data into the DB from its associated [T]. This + * will override the default. + */ + fun modelAdapterModelSaver(modelSaver: ModelSaver) = apply { + this.modelAdapterModelSaver = modelSaver + } + + /** + * Define how the table loads single models. This will override the default. + */ + fun singleModelLoader(singleModelLoader: SingleModelLoader) = apply { + this.singleModelLoader = singleModelLoader + } + + /** + * Define how the table loads a [List] of items. This will override the default. + */ + fun listModelLoader(listModelLoader: ListModelLoader) = apply { + this.listModelLoader = listModelLoader + } + + /** + * @return A new [TableConfig]. Subsequent calls to this method produce a new instance + * of [TableConfig]. + */ + fun build(): TableConfig<*> = TableConfig(this) + } + + companion object { + + @JvmStatic + fun builder(tableClass: Class): Builder = + Builder(tableClass) + + fun builder(tableClass: KClass): Builder = + Builder(tableClass) + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/AndroidDatabase.kt b/lib/src/main/kotlin/com/dbflow5/database/AndroidDatabase.kt new file mode 100644 index 000000000..c23214877 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/AndroidDatabase.kt @@ -0,0 +1,91 @@ +package com.dbflow5.database + +import android.content.ContentValues +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteException +import android.os.Build + +/** + * Description: Specifies the android default implementation of a database. + */ +class AndroidDatabase internal constructor(val database: SQLiteDatabase) : DatabaseWrapper { + + override fun execSQL(query: String) { + rethrowDBFlowException { database.execSQL(query) } + } + + override fun beginTransaction() { + database.beginTransaction() + } + + override fun setTransactionSuccessful() { + database.setTransactionSuccessful() + } + + override fun endTransaction() { + database.endTransaction() + } + + override val version: Int + get() = database.version + + override fun compileStatement(rawQuery: String): DatabaseStatement = rethrowDBFlowException { + AndroidDatabaseStatement.from(database.compileStatement(rawQuery), database) + } + + override fun rawQuery(query: String, selectionArgs: Array?): FlowCursor = rethrowDBFlowException { + FlowCursor.from(database.rawQuery(query, selectionArgs)) + } + + override fun updateWithOnConflict(tableName: String, + contentValues: ContentValues, + where: String?, + whereArgs: Array?, + conflictAlgorithm: Int): Long + = rethrowDBFlowException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + database.updateWithOnConflict(tableName, contentValues, where, whereArgs, conflictAlgorithm).toLong() + } else { + database.update(tableName, contentValues, where, whereArgs).toLong() + } + } + + override fun insertWithOnConflict(tableName: String, + nullColumnHack: String?, + values: ContentValues, + sqLiteDatabaseAlgorithmInt: Int): Long = rethrowDBFlowException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + database.insertWithOnConflict(tableName, nullColumnHack, values, sqLiteDatabaseAlgorithmInt) + } else { + database.insert(tableName, nullColumnHack, values) + } + } + + override fun query(tableName: String, + columns: Array?, + selection: String?, + selectionArgs: Array?, + groupBy: String?, + having: String?, + orderBy: String?): FlowCursor = rethrowDBFlowException { + FlowCursor.from(database.query(tableName, columns, selection, selectionArgs, groupBy, having, orderBy)) + } + + override fun delete(tableName: String, whereClause: String?, whereArgs: Array?): Int = rethrowDBFlowException { + database.delete(tableName, whereClause, whereArgs) + } + + companion object { + + @JvmStatic + fun from(database: SQLiteDatabase): AndroidDatabase = AndroidDatabase(database) + } +} + +fun SQLiteException.toDBFlowSQLiteException() = com.dbflow5.database.SQLiteException("A Database Error Occurred", this) + +inline fun rethrowDBFlowException(fn: () -> T) = try { + fn() +} catch (e: SQLiteException) { + throw e.toDBFlowSQLiteException() +} \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/database/AndroidDatabaseStatement.kt b/lib/src/main/kotlin/com/dbflow5/database/AndroidDatabaseStatement.kt new file mode 100644 index 000000000..5caee9af5 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/AndroidDatabaseStatement.kt @@ -0,0 +1,86 @@ +package com.dbflow5.database + +import android.database.Cursor +import android.database.SQLException +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteStatement +import android.os.Build + +/** + * Description: + */ +class AndroidDatabaseStatement +internal constructor(val statement: SQLiteStatement, + private val database: SQLiteDatabase) : BaseDatabaseStatement(), DatabaseStatement { + + override fun executeUpdateDelete(): Long { + var count: Long = 0 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + count = statement.executeUpdateDelete().toLong() + } else { + statement.execute() + + var cursor: Cursor? = null + try { + cursor = database.rawQuery("SELECT changes() AS affected_row_count", null) + if (cursor != null && cursor.count > 0 && cursor.moveToFirst()) { + count = cursor.getLong(cursor.getColumnIndex("affected_row_count")) + } + } catch (e: SQLException) { + // Handle exception here. + } finally { + cursor?.close() + } + } + return count + } + + override fun execute() { + statement.execute() + } + + override fun close() { + statement.close() + } + + override fun simpleQueryForLong(): Long = rethrowDBFlowException { + val simpleQueryForLong = statement.simpleQueryForLong() + simpleQueryForLong + } + + override fun simpleQueryForString(): String? = rethrowDBFlowException { statement.simpleQueryForString() } + + override fun executeInsert(): Long = rethrowDBFlowException { statement.executeInsert() } + + override fun bindString(index: Int, s: String) { + statement.bindString(index, s) + } + + override fun bindNull(index: Int) { + statement.bindNull(index) + } + + override fun bindLong(index: Int, aLong: Long) { + statement.bindLong(index, aLong) + } + + override fun bindDouble(index: Int, aDouble: Double) { + statement.bindDouble(index, aDouble) + } + + override fun bindBlob(index: Int, bytes: ByteArray) { + statement.bindBlob(index, bytes) + } + + override fun bindAllArgsAsStrings(selectionArgs: Array?) { + statement.bindAllArgsAsStrings(selectionArgs) + } + + companion object { + + @JvmStatic + fun from(sqLiteStatement: SQLiteStatement, + database: SQLiteDatabase): AndroidDatabaseStatement = + AndroidDatabaseStatement(sqLiteStatement, database) + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/AndroidSQLiteOpenHelper.kt b/lib/src/main/kotlin/com/dbflow5/database/AndroidSQLiteOpenHelper.kt new file mode 100644 index 000000000..05a76070b --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/AndroidSQLiteOpenHelper.kt @@ -0,0 +1,157 @@ +package com.dbflow5.database + +import android.content.Context +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteOpenHelper +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.config.OpenHelperCreator + +/** + * Description: Wraps around the [SQLiteOpenHelper] and provides extra features for use in this library. + */ +open class AndroidSQLiteOpenHelper( + private val context: Context, + databaseDefinition: DBFlowDatabase, + listener: DatabaseCallback?) + : SQLiteOpenHelper(context, + if (databaseDefinition.isInMemory) null else databaseDefinition.databaseFileName, + null, + databaseDefinition.databaseVersion), OpenHelper { + + private val databaseHelperDelegate: DatabaseHelperDelegate + private var androidDatabase: AndroidDatabase? = null + private val _databaseName = databaseDefinition.databaseFileName + + init { + var backupHelper: OpenHelper? = null + if (databaseDefinition.backupEnabled()) { + // Temp database mirrors existing + backupHelper = BackupHelper(context, + DatabaseHelperDelegate.getTempDbFileName(databaseDefinition), + databaseDefinition.databaseVersion, databaseDefinition) + } + + databaseHelperDelegate = DatabaseHelperDelegate(context, listener, databaseDefinition, backupHelper) + } + + override fun performRestoreFromBackup() { + databaseHelperDelegate.performRestoreFromBackup() + } + + override val delegate: DatabaseHelperDelegate? + get() = databaseHelperDelegate + + override val isDatabaseIntegrityOk: Boolean + get() = databaseHelperDelegate.isDatabaseIntegrityOk + + override fun backupDB() { + databaseHelperDelegate.backupDB() + } + + override val database: DatabaseWrapper + get() { + if (androidDatabase == null || androidDatabase?.database?.isOpen == false) { + androidDatabase = AndroidDatabase.from(writableDatabase) + } + return androidDatabase!! + } + + /** + * Set a listener to listen for specific DB events and perform an action before we execute this classes + * specific methods. + * + * @param callback + */ + override fun setDatabaseListener(callback: DatabaseCallback?) { + databaseHelperDelegate.setDatabaseHelperListener(callback) + } + + override fun onCreate(db: SQLiteDatabase) { + databaseHelperDelegate.onCreate(AndroidDatabase.from(db)) + } + + override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + databaseHelperDelegate.onUpgrade(AndroidDatabase.from(db), oldVersion, newVersion) + } + + override fun onOpen(db: SQLiteDatabase) { + databaseHelperDelegate.onOpen(AndroidDatabase.from(db)) + } + + override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + databaseHelperDelegate.onDowngrade(AndroidDatabase.from(db), oldVersion, newVersion) + } + + override fun closeDB() { + androidDatabase?.database?.close() + } + + override fun deleteDB() { + context.deleteDatabase(_databaseName) + } + + /** + * Simple helper to manage backup. + */ + private inner class BackupHelper(context: Context, + name: String, version: Int, + databaseDefinition: DBFlowDatabase) + : SQLiteOpenHelper(context, name, null, version), OpenHelper { + + private var androidDatabase: AndroidDatabase? = null + private val baseDatabaseHelper: BaseDatabaseHelper = BaseDatabaseHelper(context, databaseDefinition) + private val _databaseName = databaseDefinition.databaseFileName + + override val database: DatabaseWrapper + get() { + if (androidDatabase == null) { + androidDatabase = AndroidDatabase.from(writableDatabase) + } + return androidDatabase!! + } + + override fun performRestoreFromBackup() = Unit + + override val delegate: DatabaseHelperDelegate? + get() = null + + override val isDatabaseIntegrityOk: Boolean + get() = false + + override fun backupDB() {} + + override fun setDatabaseListener(callback: DatabaseCallback?) {} + + override fun onCreate(db: SQLiteDatabase) { + baseDatabaseHelper.onCreate(AndroidDatabase.from(db)) + } + + override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + baseDatabaseHelper.onUpgrade(AndroidDatabase.from(db), oldVersion, newVersion) + } + + override fun onOpen(db: SQLiteDatabase) { + baseDatabaseHelper.onOpen(AndroidDatabase.from(db)) + } + + override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + baseDatabaseHelper.onDowngrade(AndroidDatabase.from(db), oldVersion, newVersion) + } + + override fun closeDB() = Unit + + override fun deleteDB() { + context.deleteDatabase(_databaseName) + } + } + + companion object { + @JvmStatic + fun createHelperCreator(context: Context): OpenHelperCreator = + { db: DBFlowDatabase, databaseCallback: DatabaseCallback? -> + AndroidSQLiteOpenHelper(context, db, databaseCallback) + } + } + +} + diff --git a/lib/src/main/kotlin/com/dbflow5/database/BaseDatabaseHelper.kt b/lib/src/main/kotlin/com/dbflow5/database/BaseDatabaseHelper.kt new file mode 100644 index 000000000..dd55328ca --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/BaseDatabaseHelper.kt @@ -0,0 +1,205 @@ +package com.dbflow5.database + +import android.content.Context +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.config.FlowLog +import com.dbflow5.config.NaturalOrderComparator +import java.io.IOException +import java.io.InputStream + +/** + * Description: + */ +open class BaseDatabaseHelper(protected val context: Context, + val databaseDefinition: DBFlowDatabase) { + + open fun onCreate(db: DatabaseWrapper) { + checkForeignKeySupport(db) + executeTableCreations(db) + executeMigrations(db, -1, db.version) + executeViewCreations(db) + } + + open fun onUpgrade(db: DatabaseWrapper, oldVersion: Int, newVersion: Int) { + checkForeignKeySupport(db) + executeTableCreations(db) + executeMigrations(db, oldVersion, newVersion) + executeViewCreations(db) + } + + open fun onOpen(db: DatabaseWrapper) { + checkForeignKeySupport(db) + } + + open fun onDowngrade(db: DatabaseWrapper, oldVersion: Int, newVersion: Int) { + checkForeignKeySupport(db) + } + + /** + * If foreign keys are supported, we turn it on the DB specified. + */ + protected fun checkForeignKeySupport(database: DatabaseWrapper) { + if (databaseDefinition.isForeignKeysSupported) { + database.execSQL("PRAGMA foreign_keys=ON;") + FlowLog.log(FlowLog.Level.I, "Foreign Keys supported. Enabling foreign key features.") + } + } + + protected fun executeTableCreations(database: DatabaseWrapper) { + try { + database.beginTransaction() + val modelAdapters = databaseDefinition.getModelAdapters() + modelAdapters + .asSequence() + .filter { it.createWithDatabase() } + .forEach { + try { + database.execSQL(it.creationQuery) + } catch (e: SQLiteException) { + FlowLog.logError(e) + } + } + database.setTransactionSuccessful() + } finally { + database.endTransaction() + } + } + + /** + * This method executes CREATE TABLE statements as well as CREATE VIEW on the database passed. + */ + protected fun executeViewCreations(database: DatabaseWrapper) { + try { + database.beginTransaction() + val modelViews = databaseDefinition.modelViewAdapters + modelViews + .asSequence() + .map { "CREATE VIEW IF NOT EXISTS ${it.viewName} AS ${it.getCreationQuery(database)}" } + .forEach { + try { + database.execSQL(it) + } catch (e: SQLiteException) { + FlowLog.logError(e) + } + } + database.setTransactionSuccessful() + } finally { + database.endTransaction() + } + } + + protected fun executeMigrations(db: DatabaseWrapper, + oldVersion: Int, newVersion: Int) { + + // will try migrations file or execute migrations from code + try { + val files: List = context.assets.list( + "$MIGRATION_PATH/${databaseDefinition.databaseName}") + .sortedWith(com.dbflow5.config.NaturalOrderComparator()) + + val migrationFileMap = hashMapOf>() + for (file in files) { + try { + val version = Integer.valueOf(file.replace(".sql", "")) + val fileList = migrationFileMap.getOrPut(version) { arrayListOf() } + fileList.add(file) + } catch (e: NumberFormatException) { + FlowLog.log(FlowLog.Level.W, "Skipping invalidly named file: $file", e) + } + + } + + val migrationMap = databaseDefinition.migrations + + val curVersion = oldVersion + 1 + + try { + db.beginTransaction() + + // execute migrations in order, migration file first before wrapped migration classes. + for (i in curVersion..newVersion) { + val migrationFiles = migrationFileMap[i] + if (migrationFiles != null) { + for (migrationFile in migrationFiles) { + executeSqlScript(db, migrationFile) + FlowLog.log(FlowLog.Level.I, "$migrationFile executed successfully.") + } + } + + val migrationsList = migrationMap[i] + if (migrationsList != null) { + for (migration in migrationsList) { + // before migration + migration.onPreMigrate() + + // migrate + migration.migrate(db) + + // after migration cleanup + migration.onPostMigrate() + FlowLog.log(FlowLog.Level.I, "${migration.javaClass} executed successfully.") + } + } + } + db.setTransactionSuccessful() + } finally { + db.endTransaction() + } + } catch (e: IOException) { + FlowLog.log(FlowLog.Level.E, "Failed to execute migrations. App might be in an inconsistent state.", e) + } + + } + + /** + * Supports multiline sql statements with ended with the standard ";" + * + * @param db The database to run it on + * @param file the file name in assets/migrations that we read from + */ + private fun executeSqlScript(db: DatabaseWrapper, + file: String) { + try { + val input: InputStream = context.assets.open("$MIGRATION_PATH/${databaseDefinition.databaseName}/$file") + + // ends line with SQL + val querySuffix = ";" + + // standard java comments + val queryCommentPrefix = "--" + var query = StringBuffer() + + input.reader().buffered().forEachLine { fileLine -> + var line = fileLine.trim { it <= ' ' } + val isEndOfQuery = line.endsWith(querySuffix) + if (line.startsWith(queryCommentPrefix)) { + return@forEachLine + } + if (isEndOfQuery) { + line = line.substring(0, line.length - querySuffix.length) + } + query.append(" ").append(line) + if (isEndOfQuery) { + db.execSQL(query.toString()) + query = StringBuffer() + } + } + + val queryString = query.toString() + if (queryString.trim { it <= ' ' }.isNotEmpty()) { + db.execSQL(queryString) + } + } catch (e: IOException) { + FlowLog.log(FlowLog.Level.E, "Failed to execute $file. App might be in an inconsistent state!", e) + } + } + + companion object { + + /** + * Location where the migration files should exist. + */ + @JvmStatic + val MIGRATION_PATH = "migrations" + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/BaseDatabaseStatement.kt b/lib/src/main/kotlin/com/dbflow5/database/BaseDatabaseStatement.kt new file mode 100644 index 000000000..f04a59d21 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/BaseDatabaseStatement.kt @@ -0,0 +1,52 @@ +package com.dbflow5.database + +/** + * Description: Default implementation for some [DatabaseStatement] methods. + */ +abstract class BaseDatabaseStatement : DatabaseStatement { + + override fun bindStringOrNull(index: Int, s: String?) { + if (s != null) { + bindString(index, s) + } else { + bindNull(index) + } + } + + override fun bindNumber(index: Int, number: Number?) { + bindNumberOrNull(index, number) + } + + override fun bindNumberOrNull(index: Int, number: Number?) { + if (number != null) { + bindLong(index, number.toLong()) + } else { + bindNull(index) + } + } + + override fun bindDoubleOrNull(index: Int, aDouble: Double?) { + if (aDouble != null) { + bindDouble(index, aDouble) + } else { + bindNull(index) + } + } + + override fun bindFloatOrNull(index: Int, aFloat: Float?) { + if (aFloat != null) { + bindDouble(index, aFloat.toDouble()) + } else { + bindNull(index) + } + } + + override fun bindBlobOrNull(index: Int, bytes: ByteArray?) { + if (bytes != null) { + bindBlob(index, bytes) + } else { + bindNull(index) + } + } + +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/ContentValueExtensions.kt b/lib/src/main/kotlin/com/dbflow5/database/ContentValueExtensions.kt new file mode 100644 index 000000000..4a86bcbf3 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/ContentValueExtensions.kt @@ -0,0 +1,24 @@ +package com.dbflow5.database + +import android.content.ContentValues + +operator fun ContentValues.set(key: String, value: String?) = put(key, value) + +operator fun ContentValues.set(key: String, value: Byte?) = put(key, value) + +operator fun ContentValues.set(key: String, value: Short?) = put(key, value) + +operator fun ContentValues.set(key: String, value: Int?) = put(key, value) + +operator fun ContentValues.set(key: String, value: Long?) = put(key, value) + +operator fun ContentValues.set(key: String, value: Float?) = put(key, value) + +operator fun ContentValues.set(key: String, value: Double?) = put(key, value) + +operator fun ContentValues.set(key: String, value: Boolean?) = put(key, value) + +operator fun ContentValues.set(key: String, value: ByteArray?) = put(key, value) + + + diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperListener.java b/lib/src/main/kotlin/com/dbflow5/database/DatabaseCallback.kt similarity index 59% rename from dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperListener.java rename to lib/src/main/kotlin/com/dbflow5/database/DatabaseCallback.kt index 7d787642a..d1f0c9b60 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/structure/database/DatabaseHelperListener.java +++ b/lib/src/main/kotlin/com/dbflow5/database/DatabaseCallback.kt @@ -1,25 +1,23 @@ -package com.raizlabs.android.dbflow.structure.database; - -import android.support.annotation.NonNull; +package com.dbflow5.database /** - * Description: Provides callbacks for {@link OpenHelper} methods + * Description: Provides callbacks for [OpenHelper] methods */ -public interface DatabaseHelperListener { +interface DatabaseCallback { /** * Called when the DB is opened * * @param database The database that is opened */ - void onOpen(@NonNull DatabaseWrapper database); + fun onOpen(database: DatabaseWrapper) /** * Called when the DB is created * * @param database The database that is created */ - void onCreate(@NonNull DatabaseWrapper database); + fun onCreate(database: DatabaseWrapper) /** * Called when the DB is upgraded. @@ -28,7 +26,7 @@ public interface DatabaseHelperListener { * @param oldVersion The previous DB version * @param newVersion The new DB version */ - void onUpgrade(@NonNull DatabaseWrapper database, int oldVersion, int newVersion); + fun onUpgrade(database: DatabaseWrapper, oldVersion: Int, newVersion: Int) /** * Called when DB is downgraded. Note that this may not be supported by all implementations of the DB. @@ -37,5 +35,5 @@ public interface DatabaseHelperListener { * @param oldVersion The old. higher version. * @param newVersion The new lower version. */ - void onDowngrade(@NonNull DatabaseWrapper databaseWrapper, int oldVersion, int newVersion); + fun onDowngrade(databaseWrapper: DatabaseWrapper, oldVersion: Int, newVersion: Int) } diff --git a/lib/src/main/kotlin/com/dbflow5/database/DatabaseHelperDelegate.kt b/lib/src/main/kotlin/com/dbflow5/database/DatabaseHelperDelegate.kt new file mode 100644 index 000000000..11b7d9193 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/DatabaseHelperDelegate.kt @@ -0,0 +1,286 @@ +package com.dbflow5.database + +import android.content.Context +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.config.FlowLog +import com.dbflow5.transaction.DefaultTransactionQueue +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream + +/** + * Description: An abstraction from some parts of the Android SQLiteOpenHelper where this can be + * used in other helper class definitions. + */ +class DatabaseHelperDelegate( + context: Context, + private var databaseCallback: DatabaseCallback?, + databaseDefinition: DBFlowDatabase, + private val backupHelper: OpenHelper?) + : BaseDatabaseHelper(context, databaseDefinition) { + + /** + * @return the temporary database file name for when we have backups enabled + * [DBFlowDatabase.backupEnabled] + */ + private val tempDbFileName: String + get() = getTempDbFileName(databaseDefinition) + + /** + * Pulled partially from code, it runs a "PRAGMA quick_check(1)" to see if the database is ok. + * This method will [.restoreBackUp] if they are enabled on the database if this check fails. So + * use with caution and ensure that you backup the database often! + * + * @return true if the database is ok, false if the consistency has been compromised. + */ + val isDatabaseIntegrityOk: Boolean + get() = isDatabaseIntegrityOk(writableDatabase) + + val writableDatabase: DatabaseWrapper + get() = databaseDefinition + + fun performRestoreFromBackup() { + movePrepackagedDB(databaseDefinition.databaseFileName, + databaseDefinition.databaseFileName) + + if (databaseDefinition.backupEnabled()) { + if (backupHelper == null) { + throw IllegalStateException("the passed backup helper was null, even though backup" + + " is enabled. Ensure that its passed in.") + } + restoreDatabase(tempDbFileName, databaseDefinition.databaseFileName) + backupHelper.database + } + } + + /** + * @param databaseCallback Listens for operations the DB and allow you to provide extra + * functionality. + */ + fun setDatabaseHelperListener(databaseCallback: DatabaseCallback?) { + this.databaseCallback = databaseCallback + } + + override fun onCreate(db: DatabaseWrapper) { + databaseCallback?.onCreate(db) + super.onCreate(db) + } + + override fun onUpgrade(db: DatabaseWrapper, oldVersion: Int, newVersion: Int) { + databaseCallback?.onUpgrade(db, oldVersion, newVersion) + super.onUpgrade(db, oldVersion, newVersion) + } + + override fun onOpen(db: DatabaseWrapper) { + databaseCallback?.onOpen(db) + super.onOpen(db) + } + + override fun onDowngrade(db: DatabaseWrapper, oldVersion: Int, newVersion: Int) { + databaseCallback?.onDowngrade(db, oldVersion, newVersion) + super.onDowngrade(db, oldVersion, newVersion) + } + + /** + * Copies over the prepackaged DB into the main DB then deletes the existing DB to save storage space. If + * we have a backup that exists + * + * @param databaseName The name of the database to copy over + * @param prepackagedName The name of the prepackaged db file + */ + fun movePrepackagedDB(databaseName: String, prepackagedName: String) { + val dbPath = context.getDatabasePath(databaseName) + + // If the database already exists, and is ok return + if (dbPath.exists() && (!databaseDefinition.areConsistencyChecksEnabled() || databaseDefinition.areConsistencyChecksEnabled() && isDatabaseIntegrityOk(writableDatabase))) { + return + } + + // Make sure we have a path to the file + dbPath.parentFile.mkdirs() + + // Try to copy database file + try { + // check existing and use that as backup + val existingDb = context.getDatabasePath(tempDbFileName) + val inputStream: InputStream + // if it exists and the integrity is ok we use backup as the main DB is no longer valid + inputStream = if (existingDb.exists() + && (!databaseDefinition.backupEnabled() || + (databaseDefinition.backupEnabled() + && backupHelper != null + && isDatabaseIntegrityOk(backupHelper.database)))) { + FileInputStream(existingDb) + } else { + context.assets.open(prepackagedName) + } + writeDB(dbPath, inputStream) + } catch (e: IOException) { + FlowLog.log(FlowLog.Level.W, "Failed to open file", e) + } + + } + + /** + * Pulled partially from code, it runs a "PRAGMA quick_check(1)" to see if the database is ok. + * This method will [.restoreBackUp] if they are enabled on the database if this check fails. So + * use with caution and ensure that you backup the database often! + * + * @return true if the database is ok, false if the consistency has been compromised. + */ + fun isDatabaseIntegrityOk(databaseWrapper: DatabaseWrapper): Boolean { + var integrityOk = true + + var statement: DatabaseStatement? = null + try { + statement = databaseWrapper.compileStatement("PRAGMA quick_check(1)") + val result = statement.simpleQueryForString() + if (result == null || !result.equals("ok", ignoreCase = true)) { + // integrity_checker failed on main or attached databases + FlowLog.log(FlowLog.Level.E, "PRAGMA integrity_check on ${databaseDefinition.databaseName} returned: $result") + integrityOk = false + if (databaseDefinition.backupEnabled()) { + integrityOk = restoreBackUp() + } + } + } finally { + if (statement != null) { + statement.close() + } + } + return integrityOk + } + + /** + * If integrity check fails, this method will use the backup db to fix itself. In order to prevent + * loss of data, please backup often! + */ + fun restoreBackUp(): Boolean { + var success = true + + val db = context.getDatabasePath(TEMP_DB_NAME + databaseDefinition.databaseName) + val corrupt = context.getDatabasePath(databaseDefinition.databaseName) + if (corrupt.delete()) { + try { + writeDB(corrupt, FileInputStream(db)) + } catch (e: IOException) { + FlowLog.logError(e) + success = false + } + + } else { + FlowLog.log(FlowLog.Level.E, "Failed to delete DB") + } + return success + } + + /** + * Writes the [InputStream] of the existing db to the file specified. + * + * @param dbPath The file to write to. + * @param existingDB The existing database file's input stream¬ + * @throws IOException + */ + @Throws(IOException::class) + private fun writeDB(dbPath: File, existingDB: InputStream) { + val output = FileOutputStream(dbPath) + + val buffer = ByteArray(1024) + var length: Int = existingDB.read(buffer) + while (length > 0) { + output.write(buffer, 0, length) + length = existingDB.read(buffer) + } + + output.flush() + output.close() + existingDB.close() + } + + /** + * Will use the already existing app database if [DBFlowDatabase.backupEnabled] is true. If the existing + * is not there we will try to use the prepackaged database for that purpose. + * + * @param databaseName The name of the database to restore + * @param prepackagedName The name of the prepackaged db file + */ + fun restoreDatabase(databaseName: String, prepackagedName: String) { + val dbPath = context.getDatabasePath(databaseName) + + // If the database already exists, return + if (dbPath.exists()) { + return + } + + // Make sure we have a path to the file + dbPath.parentFile.mkdirs() + + // Try to copy database file + try { + // check existing and use that as backup + val existingDb = context.getDatabasePath(databaseDefinition.databaseFileName) + val inputStream: InputStream + // if it exists and the integrity is ok + inputStream = if (existingDb.exists() + && (databaseDefinition.backupEnabled() && backupHelper != null + && isDatabaseIntegrityOk(backupHelper.database))) { + FileInputStream(existingDb) + } else { + context.assets.open(prepackagedName) + } + writeDB(dbPath, inputStream) + } catch (e: IOException) { + FlowLog.logError(e) + } + + } + + + /** + * Saves the database as a backup on the [DefaultTransactionQueue]. + * This will create a THIRD database to use as a backup to the backup in case somehow the overwrite fails. + */ + fun backupDB() { + if (!databaseDefinition.backupEnabled() || !databaseDefinition.areConsistencyChecksEnabled()) { + throw IllegalStateException("Backups are not enabled for : " + + "${databaseDefinition.databaseName}. Please consider adding both backupEnabled " + + "and consistency checks enabled to the Database annotation") + } + + databaseDefinition.beginTransactionAsync { + val backup = context.getDatabasePath(tempDbFileName) + val temp = context.getDatabasePath("$TEMP_DB_NAME-2-${databaseDefinition.databaseFileName}") + + // if exists we want to delete it before rename + if (temp.exists()) { + temp.delete() + } + + backup.renameTo(temp) + if (backup.exists()) { + backup.delete() + } + val existing = context.getDatabasePath(databaseDefinition.databaseFileName) + + try { + backup.parentFile.mkdirs() + writeDB(backup, FileInputStream(existing)) + temp.delete() + } catch (e: Exception) { + FlowLog.logError(e) + + } + }.execute() + + } + + companion object { + + val TEMP_DB_NAME = "temp-" + + fun getTempDbFileName(databaseDefinition: DBFlowDatabase): String = + "$TEMP_DB_NAME${databaseDefinition.databaseName}.db" + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/DatabaseStatement.kt b/lib/src/main/kotlin/com/dbflow5/database/DatabaseStatement.kt new file mode 100644 index 000000000..b599a99fe --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/DatabaseStatement.kt @@ -0,0 +1,45 @@ +package com.dbflow5.database + +import java.io.Closeable + +/** + * Description: Abstracts out an Android SQLiteStatement. + */ +interface DatabaseStatement : Closeable { + + fun executeUpdateDelete(): Long + + fun execute() + + override fun close() + + fun simpleQueryForLong(): Long + + fun simpleQueryForString(): String? + + fun executeInsert(): Long + + fun bindString(index: Int, s: String) + + fun bindStringOrNull(index: Int, s: String?) + + fun bindNull(index: Int) + + fun bindLong(index: Int, aLong: Long) + + fun bindNumber(index: Int, number: Number?) + + fun bindNumberOrNull(index: Int, number: Number?) + + fun bindDouble(index: Int, aDouble: Double) + + fun bindDoubleOrNull(index: Int, aDouble: Double?) + + fun bindFloatOrNull(index: Int, aFloat: Float?) + + fun bindBlob(index: Int, bytes: ByteArray) + + fun bindBlobOrNull(index: Int, bytes: ByteArray?) + + fun bindAllArgsAsStrings(selectionArgs: Array?) +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/DatabaseStatementWrapper.kt b/lib/src/main/kotlin/com/dbflow5/database/DatabaseStatementWrapper.kt new file mode 100644 index 000000000..2973f8cc0 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/DatabaseStatementWrapper.kt @@ -0,0 +1,53 @@ +package com.dbflow5.database + +import com.dbflow5.query.BaseQueriable +import com.dbflow5.runtime.NotifyDistributor + +/** + * Description: Delegates all of its calls to the contained [DatabaseStatement], while + * providing notification methods for when operations occur. + */ +class DatabaseStatementWrapper( + private val databaseStatement: DatabaseStatement, + private val modelQueriable: BaseQueriable) : BaseDatabaseStatement() { + + override fun executeUpdateDelete(): Long { + val affected = databaseStatement.executeUpdateDelete() + if (affected > 0) { + NotifyDistributor.get().notifyTableChanged(modelQueriable.table, + modelQueriable.primaryAction) + } + return affected + } + + override fun executeInsert(): Long { + val affected = databaseStatement.executeInsert() + if (affected > 0) { + NotifyDistributor.get().notifyTableChanged(modelQueriable.table, + modelQueriable.primaryAction) + } + return affected + } + + override fun execute() = databaseStatement.execute() + + override fun close() = databaseStatement.close() + + override fun simpleQueryForLong(): Long = databaseStatement.simpleQueryForLong() + + override fun simpleQueryForString(): String? = databaseStatement.simpleQueryForString() + + override fun bindString(index: Int, s: String) = databaseStatement.bindString(index, s) + + override fun bindNull(index: Int) = databaseStatement.bindNull(index) + + override fun bindLong(index: Int, aLong: Long) = databaseStatement.bindLong(index, aLong) + + override fun bindDouble(index: Int, aDouble: Double) = + databaseStatement.bindDouble(index, aDouble) + + override fun bindBlob(index: Int, bytes: ByteArray) = databaseStatement.bindBlob(index, bytes) + + override fun bindAllArgsAsStrings(selectionArgs: Array?) = + databaseStatement.bindAllArgsAsStrings(selectionArgs) +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/DatabaseWrapper.kt b/lib/src/main/kotlin/com/dbflow5/database/DatabaseWrapper.kt new file mode 100644 index 000000000..aa7d29b80 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/DatabaseWrapper.kt @@ -0,0 +1,44 @@ +package com.dbflow5.database + +import android.content.ContentValues + +/** + * Description: Provides a base implementation that wraps a database, so other databaseForTable engines potentially can + * be used. + */ +interface DatabaseWrapper { + + val version: Int + + fun execSQL(query: String) + + fun beginTransaction() + + fun setTransactionSuccessful() + + fun endTransaction() + + fun compileStatement(rawQuery: String): DatabaseStatement + + fun compileStatement(rawQuery: String, selectionArgs: Array?): DatabaseStatement { + return compileStatement(rawQuery).apply { bindAllArgsAsStrings(selectionArgs) } + } + + fun rawQuery(query: String, selectionArgs: Array?): FlowCursor + + fun updateWithOnConflict(tableName: String, + contentValues: ContentValues, + where: String?, + whereArgs: Array?, conflictAlgorithm: Int): Long + + fun insertWithOnConflict(tableName: String, + nullColumnHack: String?, + values: ContentValues, + sqLiteDatabaseAlgorithmInt: Int): Long + + fun query(tableName: String, columns: Array?, selection: String?, + selectionArgs: Array?, groupBy: String?, + having: String?, orderBy: String?): FlowCursor + + fun delete(tableName: String, whereClause: String?, whereArgs: Array?): Int +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/FlowCursor.kt b/lib/src/main/kotlin/com/dbflow5/database/FlowCursor.kt new file mode 100644 index 000000000..c6094f189 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/FlowCursor.kt @@ -0,0 +1,255 @@ +package com.dbflow5.database + +import android.database.Cursor +import android.database.CursorWrapper + +/** + * Common [Cursor] class that wraps cursors we use in this library with convenience loading methods. + * This is used to help cut down on generated code size and potentially decrease method count. + */ +class FlowCursor private constructor(private val cursor: Cursor) : CursorWrapper(cursor) { + + // compatibility + override fun getWrappedCursor(): Cursor = cursor + + fun getStringOrDefault(index: Int, defValue: String): String { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getString(index) + } else { + defValue + } + } + + fun getStringOrDefault(columnName: String): String? = + getStringOrDefault(cursor.getColumnIndex(columnName)) + + fun getStringOrDefault(index: Int): String? { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getString(index) + } else { + null + } + } + + fun getStringOrDefault(columnName: String, defValue: String): String = + getStringOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getIntOrDefault(columnName: String): Int = + getIntOrDefault(cursor.getColumnIndex(columnName)) + + fun getIntOrDefault(index: Int): Int { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getInt(index) + } else { + 0 + } + } + + fun getIntOrDefault(index: Int, defValue: Int): Int { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getInt(index) + } else { + defValue + } + } + + fun getIntOrDefault(columnName: String, defValue: Int): Int = + getIntOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getIntOrDefault(index: Int, defValue: Int?): Int? { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getInt(index) + } else { + defValue + } + } + + fun getIntOrDefault(columnName: String, defValue: Int?): Int? = + getIntOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getDoubleOrDefault(index: Int, defValue: Double): Double { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getDouble(index) + } else { + defValue + } + } + + fun getDoubleOrDefault(columnName: String): Double = + getDoubleOrDefault(cursor.getColumnIndex(columnName)) + + fun getDoubleOrDefault(index: Int): Double { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getDouble(index) + } else { + 0.0 + } + } + + fun getDoubleOrDefault(columnName: String, defValue: Double): Double = + getDoubleOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getDoubleOrDefault(index: Int, defValue: Double?): Double? { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getDouble(index) + } else { + defValue + } + } + + fun getDoubleOrDefault(columnName: String, defValue: Double?): Double? = + getDoubleOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getFloatOrDefault(index: Int, defValue: Float): Float { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getFloat(index) + } else { + defValue + } + } + + fun getFloatOrDefault(columnName: String): Float = + getFloatOrDefault(cursor.getColumnIndex(columnName)) + + fun getFloatOrDefault(index: Int): Float { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getFloat(index) + } else { + 0f + } + } + + fun getFloatOrDefault(columnName: String, defValue: Float): Float = + getFloatOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getFloatOrDefault(index: Int, defValue: Float?): Float? { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getFloat(index) + } else { + defValue + } + } + + fun getFloatOrDefault(columnName: String, defValue: Float?): Float? = + getFloatOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getLongOrDefault(index: Int, defValue: Long): Long { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getLong(index) + } else { + defValue + } + } + + fun getLongOrDefault(columnName: String): Long = + getLongOrDefault(cursor.getColumnIndex(columnName)) + + fun getLongOrDefault(index: Int): Long { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getLong(index) + } else { + 0 + } + } + + fun getLongOrDefault(columnName: String, defValue: Long): Long = + getLongOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getLongOrDefault(index: Int, defValue: Long?): Long? { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getLong(index) + } else { + defValue + } + } + + fun getLongOrDefault(columnName: String, defValue: Long?): Long? = + getLongOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getShortOrDefault(index: Int, defValue: Short): Short { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getShort(index) + } else { + defValue + } + } + + fun getShortOrDefault(columnName: String): Short = + getShortOrDefault(cursor.getColumnIndex(columnName)) + + fun getShortOrDefault(index: Int): Short { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getShort(index) + } else { + 0 + } + } + + fun getShortOrDefault(columnName: String, defValue: Short): Short = + getShortOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getShortOrDefault(index: Int, defValue: Short?): Short? { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getShort(index) + } else { + defValue + } + } + + fun getShortOrDefault(columnName: String, defValue: Short?): Short? = + getShortOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getBlobOrDefault(columnName: String): ByteArray? = + getBlobOrDefault(cursor.getColumnIndex(columnName)) + + fun getBlobOrDefault(index: Int): ByteArray? { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getBlob(index) + } else { + null + } + } + + fun getBlobOrDefault(index: Int, defValue: ByteArray): ByteArray { + return if (index != -1 && !cursor.isNull(index)) { + cursor.getBlob(index) + } else { + defValue + } + } + + fun getBlobOrDefault(columnName: String, defValue: ByteArray): ByteArray = + getBlobOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getBooleanOrDefault(index: Int, defValue: Boolean): Boolean { + return if (index != -1 && !cursor.isNull(index)) { + getBoolean(index) + } else { + defValue + } + } + + fun getBooleanOrDefault(columnName: String): Boolean = + getBooleanOrDefault(cursor.getColumnIndex(columnName)) + + fun getBooleanOrDefault(index: Int): Boolean { + return if (index != -1 && !cursor.isNull(index)) { + getBoolean(index) + } else { + false + } + } + + fun getBooleanOrDefault(columnName: String, defValue: Boolean): Boolean = + getBooleanOrDefault(cursor.getColumnIndex(columnName), defValue) + + fun getBoolean(index: Int): Boolean = cursor.getInt(index) == 1 + + companion object { + + @JvmStatic + fun from(cursor: Cursor): FlowCursor = cursor as? FlowCursor ?: FlowCursor(cursor) + } + +} + diff --git a/lib/src/main/kotlin/com/dbflow5/database/OpenHelper.kt b/lib/src/main/kotlin/com/dbflow5/database/OpenHelper.kt new file mode 100644 index 000000000..b48afc85a --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/OpenHelper.kt @@ -0,0 +1,25 @@ +package com.dbflow5.database + +/** + * Description: Abstracts out the [DatabaseHelperDelegate] into the one used in this library. + */ +interface OpenHelper { + + val database: DatabaseWrapper + + val delegate: DatabaseHelperDelegate? + + val isDatabaseIntegrityOk: Boolean + + fun setWriteAheadLoggingEnabled(enabled: Boolean) + + fun performRestoreFromBackup() + + fun backupDB() + + fun setDatabaseListener(callback: DatabaseCallback?) + + fun closeDB() + + fun deleteDB() +} diff --git a/lib/src/main/kotlin/com/dbflow5/database/SQLiteException.kt b/lib/src/main/kotlin/com/dbflow5/database/SQLiteException.kt new file mode 100644 index 000000000..090641daa --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/database/SQLiteException.kt @@ -0,0 +1,13 @@ +package com.dbflow5.database + +/** + * Description: DBFlow mirror to an Android SQLiteException. + */ +class SQLiteException : RuntimeException { + constructor() + + constructor(error: String) : super(error) + + constructor(error: String, cause: Throwable) : super(error, cause) +} + diff --git a/lib/src/main/kotlin/com/dbflow5/migration/AlterTableMigration.kt b/lib/src/main/kotlin/com/dbflow5/migration/AlterTableMigration.kt new file mode 100644 index 000000000..82126af73 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/migration/AlterTableMigration.kt @@ -0,0 +1,138 @@ +package com.dbflow5.migration + +import androidx.annotation.CallSuper +import com.dbflow5.appendQuotedIfNeeded +import com.dbflow5.config.FlowManager +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.select +import com.dbflow5.quoteIfNeeded +import com.dbflow5.sql.SQLiteType +import com.dbflow5.stripQuotes + +/** + * Description: Provides a very nice way to alter a single table quickly and easily. + */ +class AlterTableMigration( + /** + * The table to ALTER + */ + private val table: Class) : BaseMigration() { + + /** + * The query to rename the table with + */ + private var renameQuery: String? = null + + /** + * The columns to ALTER within a table + */ + private val internalColumnDefinitions: MutableList by lazy { mutableListOf() } + + private val columnNames: MutableList by lazy { arrayListOf() } + + /** + * The old name of the table before renaming it + */ + private var oldTableName: String? = null + + override fun migrate(database: DatabaseWrapper) { + // "ALTER TABLE " + var sql = ALTER_TABLE + val tableName = FlowManager.getTableName(table) + + // "{oldName} RENAME TO {newName}" + // Since the structure has been updated already, the manager knows only the new name. + renameQuery?.let { renameQuery -> + database.execSQL(buildString { + append(sql) + appendQuotedIfNeeded(oldTableName) + append(renameQuery) + append(tableName) + }) + } + + // We have column definitions to add here + // ADD COLUMN columnName {type} + if (internalColumnDefinitions.isNotEmpty()) { + (select from table limit 0).cursor(database)?.use { cursor -> + sql = "$sql$tableName" + for (i in internalColumnDefinitions.indices) { + val columnDefinition = internalColumnDefinitions[i] + val columnName = columnNames[i].stripQuotes() + if (cursor.getColumnIndex(columnName) == -1) { + database.execSQL("$sql ADD COLUMN $columnDefinition") + } + } + } + } + } + + @CallSuper + override fun onPostMigrate() { + // cleanup and make fields eligible for garbage collection + renameQuery = null + internalColumnDefinitions.clear() + columnNames.clear() + } + + /** + * Call this to rename a table to a new name. + * + * @param oldName The new name to call the table. + * @return This instance + */ + fun renameFrom(oldName: String) = apply { + oldTableName = oldName + renameQuery = " RENAME TO " + } + + /** + * Add a column to the DB. This does not necessarily need to be reflected in the [T], + * but it is recommended. + * + * @param sqLiteType The type of column represented in the DB. + * @param columnName The name of the column to add. Use the "_Table" class for the specified table. + * @return This instance + */ + fun addColumn(sqLiteType: SQLiteType, columnName: String) = apply { + internalColumnDefinitions.add("${columnName.quoteIfNeeded()} ${sqLiteType.name}") + columnNames.add(columnName) + } + + /** + * Add a column to the DB. This does not necessarily need to be reflected in the [T], + * but it is recommended. + * + * @param sqLiteType The type of column that pertains to an [SQLiteType] + * @param columnName The name of the column to add. Use the "$Table" class for the specified table. + * @param referenceClause The clause of the references that this foreign key points to. + * @return This instance + */ + fun addForeignKeyColumn(sqLiteType: SQLiteType, columnName: String, referenceClause: String) = apply { + internalColumnDefinitions.add("${columnName.quoteIfNeeded()} ${sqLiteType.name} REFERENCES $referenceClause") + columnNames.add(columnName) + } + + /** + * @return The query that renames the table. + */ + fun getRenameQuery(): String = buildString { + append(ALTER_TABLE) + appendQuotedIfNeeded(oldTableName) + append(renameQuery) + append(FlowManager.getTableName(table)) + } + + /** + * @return A List of column definitions that add op to a table in the DB. + */ + fun getColumnDefinitions(): List { + val sql = "$ALTER_TABLE ${FlowManager.getTableName(table)}" + return internalColumnDefinitions.map { "$sql ADD COLUMN $it" } + } + + companion object { + + const val ALTER_TABLE = "ALTER TABLE " + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/migration/BaseMigration.kt b/lib/src/main/kotlin/com/dbflow5/migration/BaseMigration.kt new file mode 100644 index 000000000..312807386 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/migration/BaseMigration.kt @@ -0,0 +1,21 @@ +package com.dbflow5.migration + +import com.dbflow5.database.DatabaseWrapper + +/** + * Description: Provides the base implementation of [Migration] with + * only [Migration.migrate] needing to be implemented. + */ +abstract class BaseMigration : Migration { + + + override fun onPreMigrate() { + + } + + abstract override fun migrate(database: DatabaseWrapper) + + override fun onPostMigrate() { + + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/migration/IndexMigration.kt b/lib/src/main/kotlin/com/dbflow5/migration/IndexMigration.kt new file mode 100644 index 000000000..d91875df8 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/migration/IndexMigration.kt @@ -0,0 +1,46 @@ +package com.dbflow5.migration + +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.index +import com.dbflow5.query.property.IProperty + +/** + * Description: Defines and enables an Index structurally through a migration. + */ +abstract class IndexMigration( + /** + * The table to index on + */ + private var onTable: Class) : BaseMigration() { + + private var unique: Boolean = false + private val columns = arrayListOf>() + + abstract val name: String + + override fun migrate(database: DatabaseWrapper) { + val index = index(name, onTable).unique(unique) + columns.forEach { index.and(it) } + database.execSQL(index.query) + } + + /** + * Adds a column to the underlying INDEX + * + * @param property The name of the column to add to the Index + * @return This migration + */ + fun addColumn(property: IProperty<*>) = apply { + columns.add(property) + } + + /** + * Sets the INDEX to UNIQUE + * + * @return This migration. + */ + fun unique() = apply { + unique = true + } + +} diff --git a/lib/src/main/kotlin/com/dbflow5/migration/IndexPropertyMigration.kt b/lib/src/main/kotlin/com/dbflow5/migration/IndexPropertyMigration.kt new file mode 100644 index 000000000..a1df9ac51 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/migration/IndexPropertyMigration.kt @@ -0,0 +1,24 @@ +package com.dbflow5.migration + +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.property.IndexProperty + +/** + * Description: Allows you to specify if and when an [IndexProperty] gets used or created. + */ +abstract class IndexPropertyMigration( + /** + * @return true if create the index, false to drop the index. + */ + open val shouldCreate: Boolean = true) : BaseMigration() { + + abstract val indexProperty: IndexProperty<*> + + override fun migrate(database: DatabaseWrapper) { + if (shouldCreate) { + indexProperty.createIfNotExists(database) + } else { + indexProperty.drop(database) + } + } +} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/Migration.java b/lib/src/main/kotlin/com/dbflow5/migration/Migration.kt similarity index 50% rename from dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/Migration.java rename to lib/src/main/kotlin/com/dbflow5/migration/Migration.kt index 3b4980d0a..6e2a99d86 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/migration/Migration.java +++ b/lib/src/main/kotlin/com/dbflow5/migration/Migration.kt @@ -1,29 +1,27 @@ -package com.raizlabs.android.dbflow.sql.migration; +package com.dbflow5.migration -import android.support.annotation.NonNull; - -import com.raizlabs.android.dbflow.structure.database.DatabaseWrapper; +import com.dbflow5.database.DatabaseWrapper /** - * Description: Called when the Database is migrating. We can perform custom migrations here. A {@link com.raizlabs.android.dbflow.annotation.Migration} + * Description: Called when the Database is migrating. We can perform custom migrations here. A [com.dbflow5.annotation.Migration] * is required for registering this class to automatically be called in an upgrade of the DB. */ -public interface Migration { +interface Migration { /** - * Called before we migrate data. Instantiate migration data before releasing it in {@link #onPostMigrate()} + * Called before we migrate data. Instantiate migration data before releasing it in [.onPostMigrate] */ - void onPreMigrate(); + fun onPreMigrate() /** * Perform your migrations here * * @param database The database to operate on */ - void migrate(@NonNull DatabaseWrapper database); + fun migrate(database: DatabaseWrapper) /** * Called after the migration completes. Release migration data here. */ - void onPostMigrate(); + fun onPostMigrate() } diff --git a/lib/src/main/kotlin/com/dbflow5/migration/UpdateTableMigration.kt b/lib/src/main/kotlin/com/dbflow5/migration/UpdateTableMigration.kt new file mode 100644 index 000000000..7af1882cd --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/migration/UpdateTableMigration.kt @@ -0,0 +1,58 @@ +package com.dbflow5.migration + +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.BaseQueriable +import com.dbflow5.query.OperatorGroup +import com.dbflow5.query.SQLOperator +import com.dbflow5.query.update + +/** + * Description: Provides a simple way to update a table's field or fields quickly in a migration. + * It ties an SQLite [com.dbflow5.sql.language.Update] + * to migrations whenever we want to batch update tables in a structured manner. + */ +open class UpdateTableMigration +/** + * Creates an update migration. + * + * @param table The table to update + */ +( + /** + * The table to update + */ + private val table: Class) : BaseMigration() { + + /** + * Builds the conditions for the WHERE part of our query + */ + private val whereOperatorGroup: OperatorGroup by lazy { OperatorGroup.nonGroupingClause() } + + /** + * The conditions to use to set fields in the update query + */ + private val setOperatorGroup: OperatorGroup by lazy { OperatorGroup.nonGroupingClause() } + + val updateStatement: BaseQueriable + get() = update(table) + .set(setOperatorGroup) + .where(whereOperatorGroup) + + /** + * This will append a condition to this migration. It will execute each of these in succession with the order + * that this is called. + * + * @param conditions The conditions to append + */ + fun set(vararg conditions: SQLOperator) = apply { + setOperatorGroup.andAll(*conditions) + } + + fun where(vararg conditions: SQLOperator) = apply { + whereOperatorGroup.andAll(*conditions) + } + + override fun migrate(database: DatabaseWrapper) { + updateStatement.execute(database) + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/Actionable.kt b/lib/src/main/kotlin/com/dbflow5/query/Actionable.kt new file mode 100644 index 000000000..e31c4afc0 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Actionable.kt @@ -0,0 +1,11 @@ +package com.dbflow5.query + +import com.dbflow5.structure.ChangeAction + +/** + * Description: Provides [Action] for SQL constructs. + */ +interface Actionable { + + val primaryAction: ChangeAction +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/BaseModelQueriable.kt b/lib/src/main/kotlin/com/dbflow5/query/BaseModelQueriable.kt new file mode 100644 index 000000000..174241a0f --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/BaseModelQueriable.kt @@ -0,0 +1,100 @@ +package com.dbflow5.query + +import com.dbflow5.adapter.RetrievalAdapter +import com.dbflow5.adapter.queriable.ListModelLoader +import com.dbflow5.adapter.queriable.SingleModelLoader +import com.dbflow5.config.FlowLog +import com.dbflow5.config.FlowManager +import com.dbflow5.config.queryModelAdapter +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.list.FlowCursorList +import com.dbflow5.query.list.FlowQueryList +import com.dbflow5.sql.Query + +/** + * Description: Provides a base implementation of [ModelQueriable] to simplify a lot of code. It provides the + * default implementation for convenience. + */ +abstract class BaseModelQueriable +/** + * Constructs new instance of this class and is meant for subclasses only. + * + * @param table the table that belongs to this query. + */ +protected constructor(table: Class) + : BaseQueriable(table), ModelQueriable, Query { + + private val retrievalAdapter: RetrievalAdapter by lazy { FlowManager.getRetrievalAdapter(table) } + + private var cachingEnabled = true + + protected val listModelLoader: ListModelLoader + get() = if (cachingEnabled) { + retrievalAdapter.listModelLoader + } else { + retrievalAdapter.nonCacheableListModelLoader + } + + protected val singleModelLoader: SingleModelLoader + get() = if (cachingEnabled) { + retrievalAdapter.singleModelLoader + } else { + retrievalAdapter.nonCacheableSingleModelLoader + } + + override fun disableCaching() = apply { + cachingEnabled = false + } + + override fun queryList(databaseWrapper: DatabaseWrapper): MutableList { + val query = query + FlowLog.log(FlowLog.Level.V, "Executing query: $query") + return listModelLoader.load(databaseWrapper, query)!! + } + + override fun querySingle(databaseWrapper: DatabaseWrapper): TModel? { + val query = query + FlowLog.log(FlowLog.Level.V, "Executing query: $query") + return singleModelLoader.load(databaseWrapper, query) + } + + override fun cursorList(databaseWrapper: DatabaseWrapper): FlowCursorList = + FlowCursorList.Builder(modelQueriable = this, databaseWrapper = databaseWrapper).build() + + override fun flowQueryList(databaseWrapper: DatabaseWrapper): FlowQueryList = + FlowQueryList.Builder(modelQueriable = this, databaseWrapper = databaseWrapper).build() + + override fun executeUpdateDelete(databaseWrapper: DatabaseWrapper): Long = + compileStatement(databaseWrapper).use { it.executeUpdateDelete() } + + override fun queryCustomList(queryModelClass: Class, + databaseWrapper: DatabaseWrapper) + : MutableList { + val query = query + FlowLog.log(FlowLog.Level.V, "Executing query: $query") + return getListQueryModelLoader(queryModelClass).load(databaseWrapper, query)!! + } + + override fun queryCustomSingle(queryModelClass: Class, + databaseWrapper: DatabaseWrapper) + : QueryClass? { + val query = query + FlowLog.log(FlowLog.Level.V, "Executing query: $query") + return getSingleQueryModelLoader(queryModelClass).load(databaseWrapper, query) + } + + + protected fun getListQueryModelLoader(table: Class): ListModelLoader = + if (cachingEnabled) { + table.queryModelAdapter.listModelLoader + } else { + table.queryModelAdapter.nonCacheableListModelLoader + } + + protected fun getSingleQueryModelLoader(table: Class): SingleModelLoader = + if (cachingEnabled) { + table.queryModelAdapter.singleModelLoader + } else { + table.queryModelAdapter.nonCacheableSingleModelLoader + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/BaseOperator.kt b/lib/src/main/kotlin/com/dbflow5/query/BaseOperator.kt new file mode 100644 index 000000000..bfe2ef9c9 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/BaseOperator.kt @@ -0,0 +1,227 @@ +package com.dbflow5.query + +import com.dbflow5.byteArrayToHexString +import com.dbflow5.config.FlowManager +import com.dbflow5.converter.TypeConverter +import com.dbflow5.data.Blob +import com.dbflow5.sql.Query +import com.dbflow5.sqlEscapeString + +/** + * Description: Base class for all kinds of [SQLOperator] + */ +abstract class BaseOperator internal constructor( + /** + * The column name + */ + protected var nameAlias: NameAlias?) : SQLOperator { + + /** + * The operation such as "=", "<", and more + */ + protected var operation = "" + + /** + * The value of the column we care about + */ + protected var value: Any? = null + + /** + * A custom SQL statement after the value of the Operator + */ + protected var postArg: String? = null + + /** + * An optional separator to use when chaining these together + */ + protected var separator: String? = null + + /** + * If true, the value is set and we should append it. (to prevent false positive nulls) + */ + protected var isValueSet: Boolean = false + + /** + * @return the value of the argument + */ + override fun value(): Any? = value + + /** + * @return the column name + */ + override fun columnName(): String = nameAlias?.query ?: "" + + override fun separator(separator: String): SQLOperator { + this.separator = separator + return this + } + + override fun separator(): String? = separator + + /** + * @return true if has a separator defined for this condition. + */ + override fun hasSeparator(): Boolean = separator?.isNotEmpty() ?: false + + /** + * @return the operator such as "<", ">", or "=" + */ + override fun operation(): String = operation + + /** + * @return An optional post argument for this condition + */ + fun postArgument(): String? = postArg + + /** + * @return internal alias used for subclasses. + */ + internal fun columnAlias(): NameAlias? = nameAlias + + open fun convertObjectToString(obj: Any?, appendInnerParenthesis: Boolean): String? = + convertValueToString(obj, appendInnerParenthesis) + + companion object { + + @JvmStatic + fun convertValueToString(value: Any?, appendInnerQueryParenthesis: Boolean): String? = + convertValueToString(value, appendInnerQueryParenthesis, true) + + /** + * Converts a value input into a String representation of that. + * + * + * If it has a [TypeConverter], it first will convert it's value into its [TypeConverter.getDBValue]. + * + * + * If the value is a [Number], we return a string rep of that. + * + * + * If the value is a [BaseModelQueriable] and appendInnerQueryParenthesis is true, + * we return the query wrapped in "()" + * + * + * If the value is a [NameAlias], we return the [NameAlias.getQuery] + * + * + * If the value is a [SQLOperator], we [SQLOperator.appendConditionToQuery]. + * + * + * If the value is a [Query], we simply call [Query.getQuery]. + * + * + * If the value if a [Blob] or byte[] + * + * @param value The value of the column in Model format. + * @param appendInnerQueryParenthesis if its a [BaseModelQueriable] and an inner query value + * in a condition, we append parenthesis to the query. + * @return Returns the result as a string that's safe for SQLite. + */ + @Suppress("UNCHECKED_CAST") + @JvmStatic + fun convertValueToString(value: Any?, + appendInnerQueryParenthesis: Boolean, + typeConvert: Boolean): String? { + var locVal = value + if (locVal == null) { + return "NULL" + } else { + var stringVal: String + if (typeConvert) { + val typeConverter: TypeConverter<*, Any?>? + = FlowManager.getTypeConverterForClass(locVal.javaClass) as TypeConverter<*, Any?>? + if (typeConverter != null) { + locVal = typeConverter.getDBValue(locVal) + } + } + + if (locVal is Number) { + stringVal = locVal.toString() + } else if (locVal is Enum<*>) { + stringVal = sqlEscapeString(locVal.name) + } else { + if (appendInnerQueryParenthesis && locVal is BaseModelQueriable<*>) { + stringVal = locVal.enclosedQuery + } else if (locVal is NameAlias) { + stringVal = locVal.query + } else if (locVal is SQLOperator) { + val queryBuilder = StringBuilder() + locVal.appendConditionToQuery(queryBuilder) + stringVal = queryBuilder.toString() + } else if (locVal is Query) { + stringVal = locVal.query + } else if (locVal is Blob || locVal is ByteArray) { + val bytes: ByteArray? = if (locVal is Blob) { + locVal.blob + } else { + locVal as ByteArray? + } + stringVal = "X${sqlEscapeString(byteArrayToHexString(bytes))}" + } else { + stringVal = locVal.toString() + if (stringVal != Operator.Operation.EMPTY_PARAM) { + stringVal = sqlEscapeString(stringVal) + } + } + } + + return stringVal + } + } + + /** + * Returns a string containing the tokens joined by delimiters and converted into the property + * values for a query. + * + * @param delimiter The text to join the text with. + * @param tokens an [Iterable] of objects to be joined. Strings will be formed from + * the objects by calling [.convertValueToString]. + * @return A joined string + */ + @JvmStatic + fun joinArguments(delimiter: CharSequence, + tokens: Iterable<*>, + condition: BaseOperator): String { + val sb = StringBuilder() + var firstTime = true + for (token in tokens) { + if (firstTime) { + firstTime = false + } else { + sb.append(delimiter) + } + sb.append(condition.convertObjectToString(token, false)) + } + return sb.toString() + } + + /** + * Returns a string containing the tokens converted into DBValues joined by delimiters. + * + * @param delimiter The text to join the text with. + * @param tokens an array objects to be joined. Strings will be formed from + * the objects by calling object.toString(). + * @return A joined string + */ + @JvmStatic + fun joinArguments(delimiter: CharSequence, tokens: Array): String = + tokens.joinToString(separator = delimiter) { + convertValueToString(it, false, true) ?: "" + } + + /** + * Returns a string containing the tokens converted into DBValues joined by delimiters. + * + * @param delimiter The text to join the text with. + * @param tokens an array objects to be joined. Strings will be formed from + * the objects by calling object.toString(). + * @return A joined string + */ + @JvmStatic + fun joinArguments(delimiter: CharSequence, tokens: Iterable): String = + tokens.joinToString(separator = delimiter) { + convertValueToString(it, false, true) ?: "" + } + } + +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/BaseQueriable.kt b/lib/src/main/kotlin/com/dbflow5/query/BaseQueriable.kt new file mode 100644 index 000000000..c3c5ba2d1 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/BaseQueriable.kt @@ -0,0 +1,71 @@ +package com.dbflow5.query + +import com.dbflow5.config.FlowLog +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.database.DatabaseStatementWrapper +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.database.SQLiteException +import com.dbflow5.longForQuery +import com.dbflow5.runtime.NotifyDistributor +import com.dbflow5.structure.ChangeAction + +/** + * Description: Base implementation of something that can be queried from the database. + */ +abstract class BaseQueriable protected constructor( + /** + * @return The table associated with this INSERT + */ + val table: Class) : Queriable, Actionable { + + abstract override val primaryAction: ChangeAction + + override fun longValue(databaseWrapper: DatabaseWrapper): Long { + try { + val query = query + FlowLog.log(FlowLog.Level.V, "Executing query: $query") + return longForQuery(databaseWrapper, query) + } catch (sde: SQLiteException) { + // catch exception here, log it but return 0; + FlowLog.logWarning(sde) + } + + return 0 + } + + override fun hasData(databaseWrapper: DatabaseWrapper): Boolean = longValue(databaseWrapper) > 0 + + override fun cursor(databaseWrapper: DatabaseWrapper): FlowCursor? { + if (primaryAction == ChangeAction.INSERT) { + // inserting, let's compile and insert + compileStatement(databaseWrapper).use { it.executeInsert() } + } else { + val query = query + FlowLog.log(FlowLog.Level.V, "Executing query: " + query) + databaseWrapper.execSQL(query) + } + return null + } + + override fun executeInsert(databaseWrapper: DatabaseWrapper): Long = + compileStatement(databaseWrapper).use { it.executeInsert() } + + override fun execute(databaseWrapper: DatabaseWrapper) { + val cursor = cursor(databaseWrapper) + if (cursor != null) { + cursor.close() + } else { + // we dont query, we're executing something here. + NotifyDistributor.get().notifyTableChanged(table, primaryAction) + } + } + + override fun compileStatement(databaseWrapper: DatabaseWrapper): DatabaseStatement { + val query = query + FlowLog.log(FlowLog.Level.V, "Compiling Query Into Statement: " + query) + return DatabaseStatementWrapper(databaseWrapper.compileStatement(query), this) + } + + override fun toString(): String = query +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/BaseTransformable.kt b/lib/src/main/kotlin/com/dbflow5/query/BaseTransformable.kt new file mode 100644 index 000000000..0fa16898a --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/BaseTransformable.kt @@ -0,0 +1,67 @@ +package com.dbflow5.query + +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.query.property.IProperty + +/** + * Description: Combines basic transformations and query ops into a base class. + */ +abstract class BaseTransformable +/** + * Constructs new instance of this class and is meant for subclasses only. + * + * @param table the table that belongs to this query. + */ +protected constructor(table: Class) + : BaseModelQueriable(table), Transformable, WhereBase { + + infix fun whereExists(where: Where) = where().exists(where) + + fun where(vararg conditions: SQLOperator): Where = Where(this, *conditions) + + infix fun where(condition: SQLOperator): Where = Where(this, condition) + + override fun cursor(databaseWrapper: DatabaseWrapper): FlowCursor? = where().cursor(databaseWrapper) + + override fun groupBy(vararg nameAliases: NameAlias): Where = + where().groupBy(*nameAliases) + + override fun groupBy(vararg properties: IProperty<*>): Where = + where().groupBy(*properties) + + override fun orderBy(nameAlias: NameAlias, ascending: Boolean): Where = + where().orderBy(nameAlias, ascending) + + override fun orderBy(property: IProperty<*>, ascending: Boolean): Where = + where().orderBy(property, ascending) + + override fun orderByAll(orderByList: List): Where = where().orderByAll(orderByList) + + override fun orderBy(orderBy: OrderBy): Where = where().orderBy(orderBy) + + override fun limit(count: Long): Where = where().limit(count) + + override fun offset(offset: Long): Where = where().offset(offset) + + override fun having(vararg conditions: SQLOperator): Where = where().having(*conditions) + + abstract override fun cloneSelf(): BaseTransformable + + override fun queryList(databaseWrapper: DatabaseWrapper): MutableList { + checkSelect("query") + return super.queryList(databaseWrapper) + } + + override fun querySingle(databaseWrapper: DatabaseWrapper): TModel? { + checkSelect("query") + limit(1) + return super.querySingle(databaseWrapper) + } + + private fun checkSelect(methodName: String) { + if (queryBuilderBase !is Select) { + throw IllegalArgumentException("Please use $methodName(). The beginning is not a Select") + } + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/Case.kt b/lib/src/main/kotlin/com/dbflow5/query/Case.kt new file mode 100644 index 000000000..b33f539ff --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Case.kt @@ -0,0 +1,112 @@ +package com.dbflow5.query + +import com.dbflow5.query.property.IProperty +import com.dbflow5.query.property.Property +import com.dbflow5.quoteIfNeeded +import com.dbflow5.sql.Query + +/** + * Description: Represents a SQLITE CASE argument. + */ +class Case(private val caseColumn: IProperty<*>? = null) : Query { + + private val caseConditions = arrayListOf>() + private var columnName: String? = null + private var elseValue: TReturn? = null + private var elseSpecified = false + + // when true, only WHEN value is supported. Not WHEN condition + var isEfficientCase = false + private set + + private var endSpecified = false + + override val query: String + get() { + val queryBuilder = StringBuilder(" CASE") + if (isEfficientCase) { + queryBuilder.append(" ${BaseOperator.convertValueToString(caseColumn, false)}") + } + + queryBuilder.append(caseConditions.joinToString(separator = "")) + + if (elseSpecified) { + queryBuilder.append(" ELSE ") + .append(BaseOperator.convertValueToString(elseValue, false)) + } + if (endSpecified) { + queryBuilder.append(" END ${if (columnName != null) columnName else ""}") + } + return queryBuilder.toString() + } + + internal constructor() : this(null) + + init { + if (caseColumn != null) { + isEfficientCase = true + } + } + + @JvmName("when") + infix fun whenever(sqlOperator: SQLOperator): CaseCondition { + if (isEfficientCase) { + throw IllegalStateException("When using the efficient CASE method," + + "you must pass in value only, not condition.") + } + val caseCondition = CaseCondition(this, sqlOperator) + caseConditions.add(caseCondition) + return caseCondition + } + + @JvmName("when") + infix fun whenever(whenValue: TReturn?): CaseCondition { + if (!isEfficientCase) { + throw IllegalStateException("When not using the efficient CASE method, " + + "you must pass in the SQLOperator as a parameter") + } + val caseCondition = CaseCondition(this, whenValue) + caseConditions.add(caseCondition) + return caseCondition + } + + @JvmName("when") + infix fun whenever(property: IProperty<*>): CaseCondition { + if (!isEfficientCase) { + throw IllegalStateException("When not using the efficient CASE method, " + + "you must pass in the SQLOperator as a parameter") + } + val caseCondition = CaseCondition(this, property) + caseConditions.add(caseCondition) + return caseCondition + } + + /** + * Default case here. If not specified, value will be NULL. + */ + @JvmName("_else") + infix fun `else`(elseValue: TReturn?) = apply { + this.elseValue = elseValue + elseSpecified = true // ensure its set especially if null specified. + } + + /** + * @param columnName The name of the case that we return in a column. + * @return The case completed as a property. + */ + @JvmOverloads + fun end(columnName: String? = null): Property> { + endSpecified = true + if (columnName != null) { + this.columnName = columnName.quoteIfNeeded() + } + return Property(null, NameAlias.rawBuilder(query).build()) + } + + /** + * @return The case complete as an operator. + */ + fun endAsOperator(): Operator = Operator.op(end().nameAlias) +} + +infix fun Case.end(columnName: String) = end(columnName) diff --git a/lib/src/main/kotlin/com/dbflow5/query/CaseCondition.kt b/lib/src/main/kotlin/com/dbflow5/query/CaseCondition.kt new file mode 100644 index 000000000..62e7a8514 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/CaseCondition.kt @@ -0,0 +1,66 @@ +package com.dbflow5.query + +import com.dbflow5.query.property.IProperty +import com.dbflow5.sql.Query + +/** + * Description: Represents an individual condition inside a CASE. + */ +class CaseCondition : Query { + + private val caze: Case + private val whenValue: TReturn? + private val sqlOperator: SQLOperator? + private var thenValue: TReturn? = null + private val property: IProperty<*>? + private var thenProperty: IProperty<*>? = null + private var isThenPropertySet: Boolean = false + + override val query: String + get() = buildString { + append(" WHEN ") + if (caze.isEfficientCase) { + append(BaseOperator.convertValueToString(property ?: whenValue, false)) + } else { + sqlOperator?.appendConditionToQuery(this) + } + append(" THEN ") + append(BaseOperator.convertValueToString( + if (isThenPropertySet) thenProperty else thenValue, false)) + } + + internal constructor(caze: Case, sqlOperator: SQLOperator) { + this.caze = caze + this.sqlOperator = sqlOperator + this.whenValue = null + this.property = null + } + + internal constructor(caze: Case, whenValue: TReturn?) { + this.caze = caze + this.whenValue = whenValue + this.sqlOperator = null + this.property = null + } + + internal constructor(caze: Case, property: IProperty<*>) { + this.caze = caze + this.property = property + this.whenValue = null + this.sqlOperator = null + } + + /** + * THEN part of this query, the value that gets set on column if condition is true. + */ + infix fun then(value: TReturn?): Case = caze.apply { thenValue = value } + + infix fun then(value: IProperty<*>): Case = caze.apply { + thenProperty = value + + // in case values are null in some sense. + isThenPropertySet = true + } + + override fun toString(): String = query +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/CompletedTrigger.kt b/lib/src/main/kotlin/com/dbflow5/query/CompletedTrigger.kt new file mode 100644 index 000000000..15cfa45db --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/CompletedTrigger.kt @@ -0,0 +1,50 @@ +package com.dbflow5.query + +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.dropTrigger +import com.dbflow5.sql.Query + +/** + * Description: The last piece of a TRIGGER statement, this class contains the BEGIN...END and the logic in between. + */ +class CompletedTrigger internal constructor( + /** + * The first pieces of this TRIGGER statement + */ + private val triggerMethod: TriggerMethod, triggerLogicQuery: Query) : Query { + + /** + * The query to run between the BEGIN and END of this statement + */ + private val triggerLogicQuery = arrayListOf() + + override val query: String + get() = + "${triggerMethod.query}\nBEGIN\n${triggerLogicQuery.joinToString(separator = ";\n")};\nEND" + + init { + this.triggerLogicQuery.add(triggerLogicQuery) + } + + /** + * Appends the nextStatement to this query as another line to be executed by trigger. + */ + infix fun and(nextStatement: Query) = apply { + this.triggerLogicQuery.add(nextStatement) + } + + + /** + * Turns on this trigger + */ + fun enable(databaseWrapper: DatabaseWrapper) { + databaseWrapper.execSQL(query) + } + + /** + * Disables this trigger + */ + fun disable(databaseWrapper: DatabaseWrapper) { + dropTrigger(databaseWrapper, triggerMethod.trigger.name) + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/ContentValuesListener.kt b/lib/src/main/kotlin/com/dbflow5/query/ContentValuesListener.kt new file mode 100644 index 000000000..5fa747da8 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/ContentValuesListener.kt @@ -0,0 +1,41 @@ +package com.dbflow5.query + +import android.content.ContentValues +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.annotation.Table +import com.dbflow5.structure.Model + +/** + * Description: Called after the declared [ContentValues] are binded. It enables + * us to listen and add custom behavior to the [ContentValues]. These must be + * defined in a [Model] class to register properly. + * + * + * This class will no longer get called during updates unless explicit call to + * [ModelAdapter.bindToContentValues] + * or [ModelAdapter.bindToInsertValues] with setting [Table.generateContentValues] to true. + * + * @see SQLiteStatementListener + */ +@Deprecated("") +interface ContentValuesListener { + + /** + * Called during an [Model.update] and at the end of + * [ModelAdapter.bindToContentValues] + * . It enables you to customly change the values as necessary during update to the database. + * + * @param contentValues The content values to bind to for an update. + */ + fun onBindToContentValues(contentValues: ContentValues) + + /** + * Called during an [Model.update] and at the end of + * [ModelAdapter.bindToInsertValues]. + * It enables you to customly change the values as necessary during insert + * to the database for a [ContentProvider]. + * + * @param contentValues The content values to insert into DB for a ContentProvider + */ + fun onBindToInsertValues(contentValues: ContentValues) +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/Delete.kt b/lib/src/main/kotlin/com/dbflow5/query/Delete.kt new file mode 100644 index 000000000..52e7f3fc4 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Delete.kt @@ -0,0 +1,26 @@ +package com.dbflow5.query + +import com.dbflow5.sql.Query +import kotlin.reflect.KClass + +/** + * Description: Constructs the beginning of a SQL DELETE query + */ +class Delete internal constructor() : Query { + + override val query: String + get() = "DELETE " + + /** + * Returns the new SQL FROM statement wrapper + * + * @param table The table we want to run this query from + * @param [T] The table class + * @return [T] + **/ + infix fun from(table: Class): From = From(this, table) + + infix fun from(table: KClass): From = from(table.java) + +} + diff --git a/lib/src/main/kotlin/com/dbflow5/query/ExistenceOperator.kt b/lib/src/main/kotlin/com/dbflow5/query/ExistenceOperator.kt new file mode 100644 index 000000000..95ddb8572 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/ExistenceOperator.kt @@ -0,0 +1,37 @@ +package com.dbflow5.query + +import com.dbflow5.appendQualifier +import com.dbflow5.sql.Query + +/** + * Description: The condition that represents EXISTS in a SQL statement. + */ +class ExistenceOperator(private val innerWhere: Where<*>) : SQLOperator, Query { + + override val query: String + get() = appendToQuery() + + override fun appendConditionToQuery(queryBuilder: StringBuilder) { + queryBuilder.appendQualifier("EXISTS", innerWhere.enclosedQuery) + } + + override fun columnName(): String { + throw RuntimeException("Method not valid for ExistenceOperator") + } + + override fun separator(): String? { + throw RuntimeException("Method not valid for ExistenceOperator") + } + + override fun separator(separator: String): SQLOperator { + // not used. + throw RuntimeException("Method not valid for ExistenceOperator") + } + + override fun hasSeparator(): Boolean = false + + override fun operation(): String = "" + + override fun value(): Any? = innerWhere + +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/From.kt b/lib/src/main/kotlin/com/dbflow5/query/From.kt new file mode 100644 index 000000000..915268ec0 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/From.kt @@ -0,0 +1,234 @@ +package com.dbflow5.query + +import com.dbflow5.config.FlowManager +import com.dbflow5.query.Join.JoinType +import com.dbflow5.query.property.IndexProperty +import com.dbflow5.sql.Query +import com.dbflow5.structure.ChangeAction +import kotlin.reflect.KClass +import kotlin.collections.Set as KSet + +/** + * Description: The SQL FROM query wrapper that must have a [Query] base. + */ +class From +/** + * The SQL from statement constructed. + * + * @param queryBuilderBase The base query we append this cursor to + * @param table The table this corresponds to + */ +internal constructor( + + /** + * @return The base query, usually a [Delete], [Select], or [Update] + */ + override val queryBuilderBase: Query, + table: Class, + + /** + * If specified, we use this as the subquery for the FROM statement. + */ + private val modelQueriable: ModelQueriable? = null) + : BaseTransformable(table) { + + /** + * An alias for the table + */ + private var tableAlias: NameAlias? = null + + /** + * Enables the SQL JOIN statement + */ + private val joins = arrayListOf>() + + override val primaryAction: ChangeAction + get() = if (queryBuilderBase is Delete) ChangeAction.DELETE else ChangeAction.CHANGE + + override val query: String + get() { + val queryBuilder = StringBuilder() + .append(queryBuilderBase.query) + if (queryBuilderBase !is Update<*>) { + queryBuilder.append("FROM ") + } + + modelQueriable?.let { queryBuilder.append(it.enclosedQuery) } + ?: queryBuilder.append(getTableAlias()) + + if (queryBuilderBase is Select) { + if (!joins.isEmpty()) { + queryBuilder.append(" ") + } + joins.forEach { queryBuilder.append(it.query) } + } else { + queryBuilder.append(" ") + } + + return queryBuilder.toString() + } + + override fun cloneSelf(): From { + val from = From( + when (queryBuilderBase) { + is Select -> queryBuilderBase.cloneSelf() + else -> queryBuilderBase + }, + table) + from.joins.addAll(joins) + from.tableAlias = tableAlias + return from + } + + /** + * @return A list of [Class] that represents tables represented in this query. For every + * [Join] on another table, this adds another [Class]. + */ + val associatedTables: KSet> + get() { + val tables = linkedSetOf>(table) + joins.mapTo(tables) { it.table } + return tables + } + + private fun getTableAlias(): NameAlias = tableAlias + ?: NameAlias.Builder(FlowManager.getTableName(table)).build().also { tableAlias = it } + + /** + * Set an alias to the table name of this [From]. + */ + infix fun `as`(alias: String): From { + tableAlias = getTableAlias() + .newBuilder() + .`as`(alias) + .build() + return this + } + + /** + * Adds a join on a specific table for this query + * + * @param table The table this corresponds to + * @param joinType The type of join to use + */ + fun join(table: Class, joinType: JoinType): Join { + val join = Join(this, table, joinType) + joins.add(join) + return join + } + + /** + * Adds a join on a specific table for this query. + * + * @param modelQueriable A query we construct the [Join] from. + * @param joinType The type of join to use. + */ + fun join(modelQueriable: ModelQueriable, joinType: JoinType): Join { + val join = Join(this, joinType, modelQueriable) + joins.add(join) + return join + } + + + infix fun innerJoin(joinTable: KClass): Join = join(joinTable.java, Join.JoinType.INNER) + + infix fun crossJoin(joinTable: KClass): Join = join(joinTable.java, Join.JoinType.CROSS) + + infix fun leftOuterJoin(joinTable: KClass): Join = join(joinTable.java, Join.JoinType.LEFT_OUTER) + + infix fun naturalJoin(joinTable: KClass): Join = join(joinTable.java, Join.JoinType.NATURAL) + + /** + * Adds a [JoinType.CROSS] join on a specific table for this query. + * + * @param table The table to join on. + * @param The class of the join table. + */ + fun crossJoin(table: Class): Join = join(table, JoinType.CROSS) + + /** + * Adds a [JoinType.CROSS] join on a specific table for this query. + * + * @param modelQueriable The query to join on. + * @param The class of the join table. + */ + fun crossJoin(modelQueriable: ModelQueriable): Join = + join(modelQueriable, JoinType.CROSS) + + /** + * Adds a [JoinType.INNER] join on a specific table for this query. + * + * @param table The table to join on. + * @param The class of the join table. + */ + fun innerJoin(table: Class): Join = join(table, JoinType.INNER) + + /** + * Adds a [JoinType.INNER] join on a specific table for this query. + * + * @param modelQueriable The query to join on. + * @param The class of the join table. + */ + fun innerJoin(modelQueriable: ModelQueriable): Join = + join(modelQueriable, JoinType.INNER) + + /** + * Adds a [JoinType.LEFT_OUTER] join on a specific table for this query. + * + * @param table The table to join on. + * @param The class of the join table. + */ + fun leftOuterJoin(table: Class): Join = + join(table, JoinType.LEFT_OUTER) + + /** + * Adds a [JoinType.LEFT_OUTER] join on a specific table for this query. + * + * @param modelQueriable The query to join on. + * @param The class of the join table. + */ + fun leftOuterJoin(modelQueriable: ModelQueriable): Join = + join(modelQueriable, JoinType.LEFT_OUTER) + + + /** + * Adds a [JoinType.NATURAL] join on a specific table for this query. + * + * @param table The table to join on. + * @param The class of the join table. + */ + fun naturalJoin(table: Class): Join = + join(table, JoinType.NATURAL) + + /** + * Adds a [JoinType.NATURAL] join on a specific table for this query. + * + * @param modelQueriable The query to join on. + * @param The class of the join table. + */ + fun naturalJoin(modelQueriable: ModelQueriable): Join = + join(modelQueriable, JoinType.NATURAL) + + /** + * Begins an INDEXED BY piece of this query with the specified name. + * + * @param indexProperty The index property generated. + */ + fun indexedBy(indexProperty: IndexProperty): IndexedBy = + IndexedBy(indexProperty, this) + +} + +/** + * Extracts the [From] from a [ModelQueriable] if possible to get [From.associatedTables] + */ +@Suppress("UNCHECKED_CAST") +fun ModelQueriable.extractFrom(): From? { + return if (this is From<*>) { + this as From + } else if (this is Where<*> && whereBase is From<*>) { + whereBase as From + } else { + null + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/IConditional.kt b/lib/src/main/kotlin/com/dbflow5/query/IConditional.kt new file mode 100644 index 000000000..e73a3a34a --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/IConditional.kt @@ -0,0 +1,118 @@ +package com.dbflow5.query + +import com.dbflow5.sql.Query + +/** + * Description: Simple interface for objects that can be used as [Operator]. This class + * takes no type parameters for primitive objects. + */ +interface IConditional : Query { + + fun isNull(): Operator<*> + + fun isNotNull(): Operator<*> + + infix fun `is`(conditional: IConditional): Operator<*> + + infix fun `is`(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun eq(conditional: IConditional): Operator<*> + + infix fun eq(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun concatenate(conditional: IConditional): Operator<*> + + infix fun isNot(conditional: IConditional): Operator<*> + + infix fun isNot(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun notEq(conditional: IConditional): Operator<*> + + infix fun notEq(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun like(conditional: IConditional): Operator<*> + + infix fun like(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun notLike(conditional: IConditional): Operator<*> + + infix fun notLike(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun glob(conditional: IConditional): Operator<*> + + infix fun glob(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun like(value: String): Operator<*> + + infix fun notLike(value: String): Operator<*> + + infix fun glob(value: String): Operator<*> + + infix fun greaterThan(conditional: IConditional): Operator<*> + + infix fun greaterThan(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun greaterThanOrEq(conditional: IConditional): Operator<*> + + infix fun greaterThanOrEq(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun lessThan(conditional: IConditional): Operator<*> + + infix fun lessThan(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun lessThanOrEq(conditional: IConditional): Operator<*> + + infix fun lessThanOrEq(baseModelQueriable: BaseModelQueriable<*>): Operator<*> + + infix fun between(conditional: IConditional): Operator.Between<*> + + infix fun between(baseModelQueriable: BaseModelQueriable<*>): Operator.Between<*> + + fun `in`(firstConditional: IConditional, vararg conditionals: IConditional): Operator.In<*> + + fun `in`(firstBaseModelQueriable: BaseModelQueriable<*>, + vararg baseModelQueriables: BaseModelQueriable<*>): Operator.In<*> + + fun notIn(firstConditional: IConditional, vararg conditionals: IConditional): Operator.In<*> + + fun notIn(firstBaseModelQueriable: BaseModelQueriable<*>, + vararg baseModelQueriables: BaseModelQueriable<*>): Operator.In<*> + + infix operator fun plus(value: BaseModelQueriable<*>): Operator<*> + + infix operator fun minus(value: BaseModelQueriable<*>): Operator<*> + + infix operator fun div(value: BaseModelQueriable<*>): Operator<*> + + infix operator fun times(value: BaseModelQueriable<*>): Operator<*> + + infix operator fun rem(value: BaseModelQueriable<*>): Operator<*> +} + +infix fun IConditional.`in`(values: Array): Operator.In<*> { + return when (values.size) { + 1 -> `in`(values[0]) + else -> this.`in`(values[0], *values.sliceArray(IntRange(1, values.size))) + } +} + +infix fun IConditional.notIn(values: Array): Operator.In<*> { + return when (values.size) { + 1 -> notIn(values[0]) + else -> this.notIn(values[0], *values.sliceArray(IntRange(1, values.size))) + } +} + +infix fun IConditional.`in`(values: Array>): Operator.In<*> { + return when (values.size) { + 1 -> `in`(values[0]) + else -> this.`in`(values[0], *values.sliceArray(IntRange(1, values.size))) + } +} + +infix fun IConditional.notIn(values: Array>): Operator.In<*> { + return when (values.size) { + 1 -> notIn(values[0]) + else -> this.notIn(values[0], *values.sliceArray(IntRange(1, values.size))) + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/IOperator.kt b/lib/src/main/kotlin/com/dbflow5/query/IOperator.kt new file mode 100644 index 000000000..37622a1b9 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/IOperator.kt @@ -0,0 +1,162 @@ +package com.dbflow5.query + +import com.dbflow5.sql.Query + +/** + * Description: Interface for objects that can be used as [Operator] that have a type parameter. + */ +interface IOperator : Query, IConditional { + + /** + * Assigns the operation to "=" + * + * @param value The [T] that we express equality to. + * @return A [Operator] that represents equality between this and the parameter. + */ + infix fun `is`(value: T?): Operator + + /** + * Assigns the operation to "=". Identical to [.is] + * + * @param value The [T] that we express equality to. + * @return A [Operator] that represents equality between this and the parameter. + * @see .is + */ + infix fun eq(value: T?): Operator + + /** + * Generates a [Operator] that concatenates this [IOperator] with the [T] via "||" + * by columnName=columnName || value + * + * @param value The value to concatenate. + * @return A [<] that represents concatenation. + */ + infix fun concatenate(value: Any?): Operator + + /** + * Assigns the operation to "!=" + * + * @param value The [T] that we express inequality to. + * @return A [<] that represents inequality between this and the parameter. + */ + infix fun isNot(value: T?): Operator + + /** + * Assigns the operation to "!=" + * + * @param value The [T] that we express inequality to. + * @return A [<] that represents inequality between this and the parameter. + * @see .notEq + */ + infix fun notEq(value: T?): Operator + + /** + * Assigns operation to ">" + * + * @param value The [T] that this [IOperator] is greater than. + * @return A [<] that represents greater than between this and the parameter. + */ + infix fun greaterThan(value: T): Operator + + /** + * Assigns operation to ">=" + * + * @param value The [T] that this [IOperator] is greater than or equal to. + * @return A [<] that represents greater than or equal between this and the parameter. + */ + infix fun greaterThanOrEq(value: T): Operator + + + /** + * Assigns operation to "<" + * + * @param value The [T] that this [IOperator] is less than. + * @return A [<] that represents less than between this and the parameter. + */ + infix fun lessThan(value: T): Operator + + + /** + * Assigns operation to "<=" + * + * @param value The [T] that this [IOperator] is less than or equal to. + * @return A [<] that represents less than or equal to between this and the parameter. + */ + infix fun lessThanOrEq(value: T): Operator + + infix fun between(value: T): Operator.Between + + /** + * Turns this [IOperator] into an [.In][<]. It means that this object should + * be represented by the set of [T] provided to follow. + * + * @param firstValue The first value (required to enforce >= 1) + * @param values The rest of the values to pass optionally. + * @return A new [.In][<] built from this [IOperator]. + */ + fun `in`(firstValue: T, vararg values: T): Operator.In + + /** + * Turns this [IOperator] into an [.In][<] (not). It means that this object should NOT + * be represented by the set of [T] provided to follow. + * + * @param firstValue The first value (required to enforce >= 1) + * @param values The rest of the values to pass optionally. + * @return A new [.In][<] (not) built from this [IOperator]. + */ + fun notIn(firstValue: T, vararg values: T): Operator.In + + /** + * Turns this [IOperator] into an [.In][<]. It means that this object should + * be represented by the set of [T] provided to follow. + * + * @param values The rest of the values to pass optionally. + * @return A new [.In][<] built from this [IOperator]. + */ + infix fun `in`(values: Collection): Operator.In + + /** + * Turns this [IOperator] into an [.In][<] (not). It means that this object should NOT + * be represented by the set of [T] provided to follow. + * + * @param values The rest of the values to pass optionally. + * @return A new [.In][<] (not) built from this [IOperator]. + */ + infix fun notIn(values: Collection): Operator.In + + /** + * Adds another value and returns the operator. i.e p1 + p2 + * + * @param value the value to add. + */ + infix operator fun plus(value: T): Operator + + /** + * Subtracts another value and returns the operator. i.e p1 - p2 + * + * @param value the value to subtract. + */ + infix operator fun minus(value: T): Operator + + /** + * Divides another value and returns as the operator. i.e p1 / p2 + * + * @param value the value to divide. + * @return A new instance. + */ + infix operator fun div(value: T): Operator + + /** + * Multiplies another value and returns as the operator. i.e p1 * p2 + * + * @param value the value to multiply. + */ + infix operator fun times(value: T): Operator + + /** + * Modulous another value and returns as the operator. i.e p1 % p2 + * + * @param value the value to calculate remainder of. + */ + infix operator fun rem(value: T): Operator +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/Index.kt b/lib/src/main/kotlin/com/dbflow5/query/Index.kt new file mode 100644 index 000000000..9cd176e9a --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Index.kt @@ -0,0 +1,125 @@ +package com.dbflow5.query + +import com.dbflow5.appendList +import com.dbflow5.appendQuotedIfNeeded +import com.dbflow5.config.FlowManager +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.SQLiteException +import com.dbflow5.dropIndex +import com.dbflow5.query.property.IProperty +import com.dbflow5.sql.Query + +/** + * Description: an INDEX class that enables you to index a specific column from a table. This enables + * faster retrieval on tables, while increasing the database file size. So enable/disable these as necessary. + */ +class Index +/** + * Creates a new index with the specified name + * + * @param indexName The name of this index. + */ +( + /** + * @return The name of this index. + */ + val indexName: String, + /** + * @return The table this INDEX belongs to. + */ + val table: Class) : Query { + private val columns: MutableList = arrayListOf() + /** + * @return true if the index is unique + */ + var isUnique = false + private set + + override val query: String + get() = buildString { + append("CREATE ") + append(if (isUnique) "UNIQUE " else "") + append("INDEX IF NOT EXISTS ") + appendQuotedIfNeeded(indexName) + append(" ON ").append(FlowManager.getTableName(table)) + append("(").appendList(columns).append(")") + } + + /** + * If true, will append the UNIQUE statement to this trigger. + * + * @param unique true if unique. If created again, a [SQLiteException] is thrown. + * @return This instance. + */ + fun unique(unique: Boolean) = apply { + isUnique = unique + } + + /** + * The table to execute this Index on. + * + * @param table The table to execute index on. + * @param properties The properties to create an index for. + * @return This instance. + */ + fun on(vararg properties: IProperty<*>) = apply { + properties.forEach { and(it) } + } + + /** + * The table to execute this Index on. + * + * @param table The table to execute index on. + * @param columns The columns to create an index for. + * @return This instance. + */ + fun on(firstAlias: NameAlias, vararg columns: NameAlias) = apply { + and(firstAlias) + columns.forEach { and(it) } + } + + /** + * Appends a column to this index list. + * + * @param property The name of the column. If already exists, this op will not be added + * @return This instance. + */ + fun and(property: IProperty<*>) = apply { + if (!columns.contains(property.nameAlias)) { + columns.add(property.nameAlias) + } + } + + /** + * Appends a column to this index list. + * + * @param columnName The name of the column. If already exists, this op will not be added + * @return This instance. + */ + fun and(columnName: NameAlias) = apply { + if (!columns.contains(columnName)) { + columns.add(columnName) + } + } + + fun enable(databaseWrapper: DatabaseWrapper) { + if (columns.isEmpty()) { + throw IllegalStateException("There should be at least one column in this index") + } + databaseWrapper.execSQL(query) + } + + fun disable(databaseWrapper: DatabaseWrapper) { + dropIndex(databaseWrapper, indexName) + } +} + + +inline fun indexOn(indexName: String, + vararg property: IProperty<*>) + = index(indexName, T::class).on(*property) + +inline fun indexOn(indexName: String, firstNameAlias: NameAlias, + vararg arrayOfNameAlias: NameAlias) + = index(indexName, T::class).on(firstNameAlias, *arrayOfNameAlias) + diff --git a/lib/src/main/kotlin/com/dbflow5/query/IndexedBy.kt b/lib/src/main/kotlin/com/dbflow5/query/IndexedBy.kt new file mode 100644 index 000000000..bd469583f --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/IndexedBy.kt @@ -0,0 +1,37 @@ +package com.dbflow5.query + +import com.dbflow5.query.property.IndexProperty +import com.dbflow5.quoteIfNeeded +import com.dbflow5.sql.Query +import com.dbflow5.structure.ChangeAction + +/** + * Description: The INDEXED BY part of a SELECT/UPDATE/DELETE + */ +class IndexedBy +/** + * Creates the INDEXED BY part of the clause. + * + * @param indexProperty The index property generated. + * @param whereBase The base piece of this query + */ +(private val indexProperty: IndexProperty, + private val whereBase: WhereBase) + : BaseTransformable(whereBase.table) { + + override val queryBuilderBase: Query + get() = whereBase.queryBuilderBase + + override val query: String + get() = buildString { + append(whereBase.query) + append(" INDEXED BY ") + append(indexProperty.indexName.quoteIfNeeded()) + append(" ") + } + + override val primaryAction: ChangeAction + get() = whereBase.primaryAction + + override fun cloneSelf(): IndexedBy = IndexedBy(indexProperty, whereBase.cloneSelf()) +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/Insert.kt b/lib/src/main/kotlin/com/dbflow5/query/Insert.kt new file mode 100644 index 000000000..3351dd1f5 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Insert.kt @@ -0,0 +1,279 @@ +package com.dbflow5.query + +import android.content.ContentValues +import com.dbflow5.annotation.ConflictAction +import com.dbflow5.appendArray +import com.dbflow5.config.FlowManager +import com.dbflow5.config.modelAdapter +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.query.property.IProperty +import com.dbflow5.query.property.Property +import com.dbflow5.sql.Query +import com.dbflow5.structure.ChangeAction + +/** + * Description: The SQLite INSERT command + */ +class Insert +/** + * Constructs a new INSERT command + * + * @param table The table to insert into + */ +internal constructor(table: Class, vararg columns: Property<*>) + : BaseQueriable(table), Query { + + /** + * The columns to specify in this query (optional) + */ + private var columns: List>? = null + + /** + * The values to specify in this query + */ + private var valuesList: MutableList> = arrayListOf() + + /** + * The conflict algorithm to use to resolve inserts. + */ + private var conflictAction: ConflictAction? = ConflictAction.NONE + + private var selectFrom: From<*>? = null + + override// append FROM, which overrides values + val query: String + get() { + val queryBuilder = StringBuilder("INSERT ") + if (conflictAction != null && conflictAction != ConflictAction.NONE) { + queryBuilder.append("OR").append(" $conflictAction ") + } + queryBuilder.append("INTO") + .append(" ") + .append(FlowManager.getTableName(table)) + + columns?.let { columns -> + if (columns.isNotEmpty()) { + queryBuilder.append("(") + .appendArray(*columns.toTypedArray()) + .append(")") + } + } + val selectFrom = this.selectFrom + if (selectFrom != null) { + queryBuilder.append(" ").append(selectFrom.query) + } else { + if (valuesList.size < 1) { + throw IllegalStateException("The insert of ${FlowManager.getTableName(table)} " + + "should have at least one value specified for the insert") + } else columns?.takeIf { it.isNotEmpty() }?.let { columns -> + valuesList.asSequence() + .filter { it.size != columns.size } + .forEach { + throw IllegalStateException( + """The Insert of ${FlowManager.getTableName(table)} + |when specifying columns needs to have the same amount + |of values and columns. found ${it.size} != ${columns.size}""".trimMargin()) + } + } + + queryBuilder.append(" VALUES(") + valuesList.forEachIndexed { i, valuesList -> + if (i > 0) { + queryBuilder.append(",(") + } + queryBuilder.append(BaseOperator.joinArguments(", ", valuesList)).append(")") + } + } + + return queryBuilder.toString() + } + + init { + columns(*columns) + } + + override val primaryAction: ChangeAction + get() = ChangeAction.INSERT + + /** + * The optional columns to specify. If specified, the values length must correspond to these columns, and + * each column has a 1-1 relationship to the values. + * + * @param columns The columns to use + */ + fun columns(vararg columns: String) = apply { + val modelClassModelAdapter = table.modelAdapter + this.columns = columns.map { modelClassModelAdapter.getProperty(it) } + } + + fun columns(vararg properties: IProperty<*>) = apply { + this.columns = properties.toList() + } + + fun columns(properties: List>) = apply { + this.columns = properties + } + + /** + * @return Appends a list of columns to this INSERT statement from the associated [TModel]. + */ + fun asColumns() = apply { + columns(*table.modelAdapter.allColumnProperties) + } + + /** + * @return Appends a list of columns to this INSERT and ? as the values. + */ + fun asColumnValues() = apply { + asColumns() + columns?.let { columns -> + val values = columns.indices.map { "?" } + valuesList.add(values) + } + } + + /** + * The required values to specify. It must be non-empty and match the length of the columns when + * a set of columns are specified. + * + * @param values The non type-converted values + */ + fun values(vararg values: Any) = apply { + valuesList.add(values.toMutableList()) + } + + /** + * The required values to specify. It must be non-empty and match the length of the columns when + * a set of columns are specified. + * + * @param values The non type-converted values + */ + fun values(values: List) = apply { + valuesList.add(values.toMutableList()) + } + + /** + * Uses the [Operator] pairs to fill this insert query. + * + * @param conditions The conditions that we use to fill the columns and values of this INSERT + */ + fun columnValues(vararg conditions: SQLOperator) = apply { + return columns(*conditions.map { it.columnName() }.toTypedArray()) + .values(conditions.map { it.value() }) + } + + /** + * Uses the [Operator] pairs to fill this insert query. + * + * @param conditions The conditions that we use to fill the columns and values of this INSERT + */ + fun columnValues(vararg conditions: Pair, *>) = apply { + return columns(*conditions.map { it.first }.toTypedArray()) + .values(conditions.map { it.second }) + } + + /** + * Uses the [Operator] pairs to fill this insert query. + * + * @param group The [Iterable] of [SQLOperator] + */ + fun columnValues(group: Iterable) = apply { + return columns(*group.map { it.columnName() }.toTypedArray()) + .values(group.map { it.value() }) + } + + fun columnValues(contentValues: ContentValues) = apply { + val entries = contentValues.valueSet() + val columns = arrayListOf() + val values = arrayListOf() + for ((key) in entries) { + columns += key + values += contentValues.get(key) + } + + return columns(*columns.toTypedArray()).values(values) + } + + /** + * Appends the specified [From], which comes from a [Select] statement. + * + * @param selectFrom The from that is continuation of [Select]. + */ + fun select(selectFrom: From<*>) = apply { + this.selectFrom = selectFrom + } + + + /** + * Specifies the optional OR method to use for this insert query + * + * @param action The conflict action to use + * @return + */ + fun or(action: ConflictAction) = apply { + conflictAction = action + } + + /** + * Specifies OR REPLACE, which will either insert if row does not exist, or replace the value if it does. + * + * @return + */ + fun orReplace() = apply { + return or(ConflictAction.REPLACE) + } + + /** + * Specifies OR ROLLBACK, which will cancel the current transaction or ABORT the current statement. + * + * @return + */ + fun orRollback() = apply { + return or(ConflictAction.ROLLBACK) + } + + /** + * Specifies OR ABORT, which will cancel the current INSERT, but all other operations will be preserved in + * the current transaction. + * + * @return + */ + fun orAbort() = apply { + return or(ConflictAction.ABORT) + } + + /** + * Specifies OR FAIL, which does not back out of the previous statements. Anything else in the current + * transaction will fail. + * + * @return + */ + fun orFail() = apply { + return or(ConflictAction.FAIL) + } + + /** + * Specifies OR IGNORE, which ignores any kind of error and proceeds as normal. + * + * @return + */ + fun orIgnore() = apply { + return or(ConflictAction.IGNORE) + } + + override fun executeUpdateDelete(databaseWrapper: DatabaseWrapper): Long { + throw IllegalStateException("Cannot call executeUpdateDelete() from an Insert") + } +} + +infix fun Insert.orReplace(into: Array, *>>) = orReplace().columnValues(*into) + +infix fun Insert.orRollback(into: Array, *>>) = orRollback().columnValues(*into) + +infix fun Insert.orAbort(into: Array, *>>) = orAbort().columnValues(*into) + +infix fun Insert.orFail(into: Array, *>>) = orFail().columnValues(*into) + +infix fun Insert.orIgnore(into: Array, *>>) = orIgnore().columnValues(*into) + +infix fun Insert.select(from: From<*>): Insert = select(from) \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/Join.kt b/lib/src/main/kotlin/com/dbflow5/query/Join.kt new file mode 100644 index 000000000..c5c9bee47 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Join.kt @@ -0,0 +1,193 @@ +package com.dbflow5.query + +import com.dbflow5.appendList +import com.dbflow5.config.FlowManager +import com.dbflow5.query.property.IProperty +import com.dbflow5.query.property.PropertyFactory +import com.dbflow5.sql.Query + +/** + * Description: Specifies a SQLite JOIN statement + */ +class Join : Query { + + val table: Class + + /** + * The type of JOIN to use + */ + private var type: JoinType + + /** + * The FROM statement that prefixes this statement. + */ + private var from: From + + /** + * The alias to name the JOIN + */ + private var alias: NameAlias + + /** + * The ON conditions + */ + private var onGroup: OperatorGroup? = null + + /** + * What columns to use. + */ + private val using = arrayListOf>() + + override// natural joins do no have on or using clauses. + val query: String + get() { + val queryBuilder = StringBuilder() + + queryBuilder.append(type.name.replace("_", " ")).append(" ") + + queryBuilder.append("JOIN") + .append(" ") + .append(alias.fullQuery) + .append(" ") + if (JoinType.NATURAL != type) { + onGroup?.let { onGroup -> + queryBuilder.append("ON") + .append(" ") + .append(onGroup.query) + .append(" ") + } ?: if (!using.isEmpty()) { + queryBuilder.append("USING (") + .appendList(using) + .append(") ") + } + } + return queryBuilder.toString() + } + + /** + * The specific type of JOIN that is used. + */ + enum class JoinType { + + /** + * an extension of the INNER JOIN. Though SQL standard defines three types of OUTER JOINs: LEFT, RIGHT, + * and FULL but SQLite only supports the LEFT OUTER JOIN. + * + * + * The OUTER JOINs have a condition that is identical to INNER JOINs, expressed using an ON, USING, or NATURAL keyword. + * The initial results table is calculated the same way. Once the primary JOIN is calculated, + * an OUTER join will take any unjoined rows from one or both tables, pad them out with NULLs, + * and append them to the resulting table. + */ + LEFT_OUTER, + + /** + * creates a new result table by combining column values of two tables (table1 and table2) based upon the join-predicate. + * The query compares each row of table1 with each row of table2 to find all pairs of rows which satisfy the join-predicate. + * When the join-predicate is satisfied, column values for each matched pair of rows of A and B are combined into a result row + */ + INNER, + + /** + * matches every row of the first table with every row of the second table. If the input tables + * have x and y columns, respectively, the resulting table will have x*y columns. + * Because CROSS JOINs have the potential to generate extremely large tables, + * care must be taken to only use them when appropriate. + */ + CROSS, + + /** + * a join that performs the same task as an INNER or LEFT JOIN, in which the ON or USING + * clause refers to all columns that the tables to be joined have in common. + */ + NATURAL + } + + constructor(from: From, table: Class, joinType: JoinType) { + this.from = from + this.table = table + type = joinType + alias = NameAlias.Builder(FlowManager.getTableName(table)).build() + } + + constructor(from: From, joinType: JoinType, + modelQueriable: ModelQueriable) { + table = modelQueriable.table + this.from = from + type = joinType + alias = PropertyFactory.from(modelQueriable).nameAlias + } + + /** + * Specifies if the JOIN has a name it should be called. + * + * @param alias The name to give it + * @return This instance + */ + fun `as`(alias: String) = apply { + this.alias = this.alias + .newBuilder() + .`as`(alias) + .build() + } + + /** + * Specify the conditions that the JOIN is on + * + * @param onConditions The conditions it is on + * @return The FROM that this JOIN came from + */ + fun on(vararg onConditions: SQLOperator): From { + checkNatural() + onGroup = OperatorGroup.nonGroupingClause().andAll(*onConditions) + return from + } + + /** + * Specify the conditions that the JOIN is on + * + * @param sqlOperator The operator that the JOIN is ON. + * @return The FROM that this JOIN came from + */ + infix fun on(sqlOperator: SQLOperator): From { + checkNatural() + onGroup = OperatorGroup.nonGroupingClause().and(sqlOperator) + return from + } + + /** + * The USING statement of the JOIN + * + * @param columns THe columns to use + * @return The FROM that this JOIN came from + */ + fun using(vararg columns: IProperty<*>): From { + checkNatural() + using.addAll(columns) + return from + } + + /** + * The USING statement of the JOIN + * + * @param property The property its using (singular). + * @return The FROM that this JOIN came from + */ + infix fun using(property: IProperty<*>): From { + checkNatural() + using.add(property) + return from + } + + /** + * @return End this [Join]. Used for [Join.JoinType.NATURAL] + */ + fun end(): From = from + + private fun checkNatural() { + if (JoinType.NATURAL == type) { + throw IllegalArgumentException("Cannot specify a clause for this join if its NATURAL." + + " Specifying a clause would have no effect. Call end() to continue the query.") + } + } +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/LoadFromCursorListener.kt b/lib/src/main/kotlin/com/dbflow5/query/LoadFromCursorListener.kt new file mode 100644 index 000000000..295dcc900 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/LoadFromCursorListener.kt @@ -0,0 +1,18 @@ +package com.dbflow5.query + +import com.dbflow5.database.FlowCursor +import com.dbflow5.structure.Model + +/** + * Description: Marks a [Model] as listening to [FlowCursor] + * events for custom handling when loading from the DB. + */ +interface LoadFromCursorListener { + + /** + * Called when the [Model] is loaded from the DB. + * + * @param cursor The cursor that is loaded. + */ + fun onLoadFromCursor(cursor: FlowCursor) +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/Method.kt b/lib/src/main/kotlin/com/dbflow5/query/Method.kt new file mode 100644 index 000000000..d49ac6567 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Method.kt @@ -0,0 +1,235 @@ +package com.dbflow5.query + + +import com.dbflow5.query.property.IProperty +import com.dbflow5.query.property.Property +import com.dbflow5.query.property.PropertyFactory +import com.dbflow5.sql.SQLiteType + +/** + * Represents SQLite methods on columns. These act as [Property] so we can use them in complex + * scenarios. + */ +open class Method(methodName: String, vararg properties: IProperty<*>) : Property(null, null as String?) { + + private val propertyList = arrayListOf>() + private val operationsList = arrayListOf() + private val methodProperty: IProperty<*> + private var _nameAlias: NameAlias? = null + + constructor(vararg properties: IProperty<*>) : this("", *properties) + + init { + methodProperty = Property(null, NameAlias.rawBuilder(methodName).build()) + + if (properties.isEmpty()) { + propertyList.add(Property.ALL_PROPERTY) + } else { + properties.forEach { addProperty(it) } + } + } + + override fun plus(property: IProperty<*>): Method = + append(property, " ${Operator.Operation.PLUS}") + + override fun minus(property: IProperty<*>): Method = + append(property, " ${Operator.Operation.MINUS}") + + override fun div(property: IProperty<*>): Property = + append(property, " ${Operator.Operation.DIVISION}") + + override fun times(property: IProperty<*>): Property = + append(property, " ${Operator.Operation.MULTIPLY}") + + override fun rem(property: IProperty<*>): Property = + append(property, " ${Operator.Operation.MOD}") + + /** + * Allows adding a property to the [Method]. Will remove the [Property.ALL_PROPERTY] + * if it exists as first item. + * + * @param property The property to add. + */ + fun addProperty(property: IProperty<*>): Method = append(property, ",") + + /** + * Appends a property with the specified operation that separates it. The operation will appear before + * the property specified. + */ + fun append(property: IProperty<*>, operation: String): Method { + // remove all property since its not needed when we specify a property. + if (propertyList.size == 1 && propertyList[0] === Property.ALL_PROPERTY) { + propertyList.removeAt(0) + } + propertyList.add(property) + operationsList.add(operation) + return this + } + + protected fun getPropertyList(): List> = propertyList + + override val nameAlias: NameAlias + get() { + var alias = _nameAlias + if (alias == null) { + var query: String? = methodProperty.query + if (query == null) { + query = "" + } + query += "(" + val propertyList = getPropertyList() + for (i in propertyList.indices) { + val property = propertyList[i] + if (i > 0) { + query += "${operationsList[i]} " + } + query += property.toString() + + } + query += ")" + alias = NameAlias.rawBuilder(query).build() + } + this._nameAlias = alias + return alias + } + + /** + * Represents the SQLite CAST operator. + */ + class Cast internal constructor(private val property: IProperty<*>) { + + /** + * @param sqLiteType The type of column to cast it to. + * @return A new [Method] that represents the statement. + */ + fun `as`(sqLiteType: SQLiteType): Property { + val newProperty = Property(property.table, + property.nameAlias + .newBuilder() + .shouldAddIdentifierToAliasName(false) + .`as`(sqLiteType.name) + .build()) + return Method("CAST", newProperty) + } + + /** + * Returns a [Property] of [Int] so it can accept [Int] values. + */ + @Suppress("UNCHECKED_CAST") + fun asInteger(): Property = `as`(SQLiteType.INTEGER) as Property + + /** + * Returns a [Property] of [Double] so it can accept [Double] values. + */ + @Suppress("UNCHECKED_CAST") + fun asReal(): Property = `as`(SQLiteType.REAL) as Property + + /** + * Returns a [Property] of [String] so it can accept [String] values. + */ + @Suppress("UNCHECKED_CAST") + fun asText(): Property = `as`(SQLiteType.TEXT) as Property + } + +} + +/** + * @param properties Set of properties that the method acts on. + * @return The average value of all properties within this group. The result is always a float from this statement + * as long as there is at least one non-NULL input. The result may be NULL if there are no non-NULL columns. + */ +fun avg(vararg properties: IProperty<*>): Method = Method("AVG", *properties) + +/** + * @param properties Set of properties that the method acts on. + * @return A count of the number of times that specified properties are not NULL in a group. Leaving + * the properties empty returns COUNT(*), which is the total number of rows in the query. + */ +fun count(vararg properties: IProperty<*>): Method = Method("COUNT", *properties) + +/** + * @param properties Set of properties that the method acts on. + * @return A string which is the concatenation of all non-NULL values of the properties. + */ +fun groupConcat(vararg properties: IProperty<*>): Method = + Method("GROUP_CONCAT", *properties) + +/** + * @param properties Set of properties that the method acts on. + * @return The method that represents the max of the specified columns/properties. + */ +fun max(vararg properties: IProperty<*>): Method = Method("MAX", *properties) + +/** + * @param properties Set of properties that the method acts on. + * @return The method that represents the min of the specified columns/properties. + */ +fun min(vararg properties: IProperty<*>): Method = Method("MIN", *properties) + +/** + * @param properties Set of properties that the method acts on. + * @return The method that represents the sum of the specified columns/properties. + */ +fun sum(vararg properties: IProperty<*>): Method = Method("SUM", *properties) + +/** + * @param properties Set of properties that the method acts on. + * @return The method that represents the total of the specified columns/properties. + */ +fun total(vararg properties: IProperty<*>): Method = Method("TOTAL", *properties) + +/** + * @param property The property to cast. + * @return A new CAST object. To complete use the [Cast. as] method. + */ +fun cast(property: IProperty<*>): Method.Cast = Method.Cast(property) + +fun replace(property: IProperty<*>, findString: String, replacement: String): Method = + Method("REPLACE", property, PropertyFactory.from(findString), PropertyFactory.from(replacement)) + +/** + * SQLite standard "strftime()" method. See SQLite documentation on this method. + */ +fun strftime(formatString: String, + timeString: String, vararg modifiers: String): Method { + val propertyList = arrayListOf>() + propertyList.add(PropertyFactory.from(formatString)) + propertyList.add(PropertyFactory.from(timeString)) + modifiers.mapTo(propertyList) { PropertyFactory.from(it) } + return Method("strftime", *propertyList.toTypedArray()) +} + +/** + * Sqlite "datetime" method. See SQLite documentation on this method. + */ +fun datetime(timeStamp: Long, vararg modifiers: String): Method { + val propertyList = arrayListOf>() + propertyList.add(PropertyFactory.from(timeStamp)) + modifiers.mapTo(propertyList) { PropertyFactory.from(it) } + return Method("datetime", *propertyList.toTypedArray()) +} + +/** + * Sqlite "date" method. See SQLite documentation on this method. + */ +fun date(timeString: String, + vararg modifiers: String): Method { + val propertyList = arrayListOf>() + propertyList.add(PropertyFactory.from(timeString)) + modifiers.mapTo(propertyList) { PropertyFactory.from(it) } + return Method("date", *propertyList.toTypedArray()) +} + +/** + * @return Constructs using the "IFNULL" method in SQLite. It will pick the first non null + * value and set that. If both are NULL then it will use NULL. + */ +fun ifNull(first: IProperty<*>, + secondIfFirstNull: IProperty<*>): Method = Method("IFNULL", first, secondIfFirstNull) + +/** + * @return Constructs using the "NULLIF" method in SQLite. If both expressions are equal, then + * NULL is set into the DB. + */ +fun nullIf(first: IProperty<*>, + second: IProperty<*>): Method = Method("NULLIF", first, second) \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/ModelQueriable.kt b/lib/src/main/kotlin/com/dbflow5/query/ModelQueriable.kt new file mode 100644 index 000000000..3cc69ebb2 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/ModelQueriable.kt @@ -0,0 +1,100 @@ +package com.dbflow5.query + +import com.dbflow5.config.DBFlowDatabase +import com.dbflow5.config.databaseForTable +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.SQLiteException +import com.dbflow5.query.list.FlowCursorList +import com.dbflow5.query.list.FlowQueryList +import com.dbflow5.structure.BaseQueryModel + + +/** + * Description: An interface for query objects to enable you to cursor from the database in a structured way. + * Examples of such statements are: [From], [Where], [StringQuery] + */ +interface ModelQueriable : Queriable { + + /** + * @return the table that this query comes from. + */ + val table: Class + + /** + * @return a list of model converted items + */ + fun queryList(databaseWrapper: DatabaseWrapper): MutableList + + /** + * @return Single model, the first of potentially many results + */ + fun querySingle(databaseWrapper: DatabaseWrapper): T? + + /** + * @return A cursor-backed list that handles conversion, retrieval, and caching of lists. Can + * cache models dynamically by setting [FlowCursorList.setCacheModels] to true. + */ + fun cursorList(databaseWrapper: DatabaseWrapper): FlowCursorList + + /** + * @return A cursor-backed [List] that handles conversion, retrieval, caching, content changes, + * and more. + */ + fun flowQueryList(databaseWrapper: DatabaseWrapper): FlowQueryList + + /** + * Returns a [List] based on the custom [TQueryModel] you pass in. + * + * @param queryModelClass The query model class to use. + * @param The class that extends [BaseueryModel] + * @return A list of custom models that are not tied to a table. + */ + fun queryCustomList(queryModelClass: Class, + databaseWrapper: DatabaseWrapper): MutableList + + /** + * Returns a single [TQueryModel] from this query. + * + * @param queryModelClass The class to use. + * @param The class that extends [BaseQueryModel] + * @return A single model from the query. + */ + fun queryCustomSingle(queryModelClass: Class, + databaseWrapper: DatabaseWrapper): TQueryModel? + + /** + * Disables caching on this query for the object retrieved from DB (if caching enabled). If + * caching is not enabled, this method is ignored. This also disables caching in a [FlowCursorList] + * or [FlowQueryList] if you [.flowQueryList] or [.cursorList] + */ + fun disableCaching(): ModelQueriable + + fun async(databaseWrapper: DBFlowDatabase, + modelQueriableFn: ModelQueriable.(DatabaseWrapper) -> R) = + databaseWrapper.beginTransactionAsync { modelQueriableFn(it) } + +} + +internal inline val ModelQueriable.enclosedQuery + get() = "(${query.trim { it <= ' ' }})" + +inline val ModelQueriable.list + get() = queryList(databaseForTable()) + +inline fun ModelQueriable<*>.customList() = + queryCustomList(T::class.java, databaseForTable()) + +inline fun ModelQueriable<*>.customSingle() = + queryCustomSingle(T::class.java, databaseForTable()) + +inline val ModelQueriable.result + get() = querySingle(databaseForTable()) + +inline val ModelQueriable.requireResult + get() = result ?: throw SQLiteException("Model result not found for $this") + +inline val ModelQueriable.flowQueryList + get() = flowQueryList(databaseForTable()) + +inline val ModelQueriable.cursorList + get() = cursorList(databaseForTable()) \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/NameAlias.kt b/lib/src/main/kotlin/com/dbflow5/query/NameAlias.kt new file mode 100644 index 000000000..b3703ee14 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/NameAlias.kt @@ -0,0 +1,249 @@ +package com.dbflow5.query + +import com.dbflow5.isNotNullOrEmpty +import com.dbflow5.quoteIfNeeded +import com.dbflow5.sql.Query +import com.dbflow5.stripQuotes + +/** + * Description: Rewritten from the ground up, this class makes it easier to build an alias. + */ +class NameAlias(private val name: String, + private val aliasName: String? = null, + val tableName: String? = null, + val keyword: String? = null, + val shouldStripIdentifier: Boolean = true, + val shouldStripAliasName: Boolean = true, + private val shouldAddIdentifierToQuery: Boolean = true, + private val shouldAddIdentifierToAliasName: Boolean = true) : Query { + + /** + * @return The name used in queries. If an alias is specified, use that, otherwise use the name + * of the property with a table name (if specified). + */ + override val query: String + get() = when { + aliasName.isNotNullOrEmpty() -> aliasName ?: "" + name.isNotNullOrEmpty() -> fullName() + else -> "" + } + + /** + * @return The value used as a key. Uses either the [.aliasNameRaw] + * or the [.nameRaw], depending on what's specified. + */ + val nameAsKey: String? + get() = if (aliasName.isNotNullOrEmpty()) { + aliasNameRaw() + } else { + nameRaw() + } + + /** + * @return The full query that represents itself with `{tableName}`.`{name}` AS `{aliasName}` + */ + val fullQuery: String + get() { + var query = fullName() + if (aliasName.isNotNullOrEmpty()) { + query += " AS ${aliasName()}" + } + if (keyword.isNotNullOrEmpty()) { + query = "$keyword $query" + } + return query + } + + private constructor(builder: Builder) : this( + name = if (builder.shouldStripIdentifier) { + builder.name.stripQuotes() ?: "" + } else { + builder.name + }, + keyword = builder.keyword, + aliasName = if (builder.shouldStripAliasName) { + builder.aliasName.stripQuotes() + } else { + builder.aliasName + }, + tableName = if (builder.tableName.isNotNullOrEmpty()) { + builder.tableName.quoteIfNeeded() + } else { + null + }, + shouldStripIdentifier = builder.shouldStripIdentifier, + shouldStripAliasName = builder.shouldStripAliasName, + shouldAddIdentifierToQuery = builder.shouldAddIdentifierToQuery, + shouldAddIdentifierToAliasName = builder.shouldAddIdentifierToAliasName) + + /** + * @return The real column name. + */ + fun name(): String? { + return if (name.isNotNullOrEmpty() && shouldAddIdentifierToQuery) + name.quoteIfNeeded() + else name + } + + /** + * @return The name, stripped from identifier syntax completely. + */ + fun nameRaw(): String = if (shouldStripIdentifier) name else name.stripQuotes() ?: "" + + /** + * @return The name used as part of the AS query. + */ + fun aliasName(): String? { + return if (aliasName.isNotNullOrEmpty() && shouldAddIdentifierToAliasName) + aliasName.quoteIfNeeded() + else aliasName + } + + /** + * @return The alias name, stripped from identifier syntax completely. + */ + fun aliasNameRaw(): String? = + if (shouldStripAliasName) aliasName else aliasName.stripQuotes() + + /** + * @return The `{tableName}`.`{name}`. If [.tableName] specified. + */ + fun fullName(): String = + (if (tableName.isNotNullOrEmpty()) "$tableName." else "") + name() + + override fun toString(): String = fullQuery + + /** + * @return Constructs a builder as a new instance that can be modified without fear. + */ + fun newBuilder(): Builder { + return Builder(name) + .keyword(keyword) + .`as`(aliasName) + .shouldStripAliasName(shouldStripAliasName) + .shouldStripIdentifier(shouldStripIdentifier) + .shouldAddIdentifierToName(shouldAddIdentifierToQuery) + .shouldAddIdentifierToAliasName(shouldAddIdentifierToAliasName) + .withTable(tableName) + } + + + class Builder(internal val name: String) { + internal var aliasName: String? = null + internal var tableName: String? = null + internal var shouldStripIdentifier = true + internal var shouldStripAliasName = true + internal var shouldAddIdentifierToQuery = true + internal var shouldAddIdentifierToAliasName = true + internal var keyword: String? = null + + /** + * Appends a DISTINCT that prefixes this alias class. + */ + fun distinct(): Builder = keyword("DISTINCT") + + /** + * Appends a keyword that prefixes this alias class. + */ + fun keyword(keyword: String?) = apply { + this.keyword = keyword + } + + /** + * Provide an alias that is used `{name}` AS `{aliasName}` + */ + fun `as`(aliasName: String?) = apply { + this.aliasName = aliasName + } + + /** + * Provide a table-name prefix as such: `{tableName}`.`{name}` + */ + fun withTable(tableName: String?) = apply { + this.tableName = tableName + } + + /** + * @param shouldStripIdentifier If true, we normalize the identifier [.name] from any + * ticks around the name. If false, we leave it as such. + */ + fun shouldStripIdentifier(shouldStripIdentifier: Boolean) = apply { + this.shouldStripIdentifier = shouldStripIdentifier + } + + /** + * @param shouldStripAliasName If true, we normalize the identifier [.aliasName] from any + * ticks around the name. If false, we leave it as such. + */ + fun shouldStripAliasName(shouldStripAliasName: Boolean) = apply { + this.shouldStripAliasName = shouldStripAliasName + } + + /** + * @param shouldAddIdentifierToName If true (default), we add the identifier to the name: `{name}` + */ + fun shouldAddIdentifierToName(shouldAddIdentifierToName: Boolean) = apply { + this.shouldAddIdentifierToQuery = shouldAddIdentifierToName + } + + /** + * @param shouldAddIdentifierToAliasName If true (default), we add an identifier to the alias + * name. `{aliasName}` + */ + fun shouldAddIdentifierToAliasName(shouldAddIdentifierToAliasName: Boolean) = apply { + this.shouldAddIdentifierToAliasName = shouldAddIdentifierToAliasName + } + + fun build(): NameAlias = NameAlias(this) + + } + + companion object { + + /** + * Combines any number of names into a single [NameAlias] separated by some operation. + * + * @param operation The operation to separate into. + * @param names The names to join. + * @return The new namealias object. + */ + fun joinNames(operation: String, vararg names: String): NameAlias { + var newName = "" + for (i in names.indices) { + if (i > 0) { + newName += " $operation " + } + newName += names[i] + } + return rawBuilder(newName).build() + } + + fun builder(name: String): Builder = Builder(name) + + fun tableNameBuilder(tableName: String): Builder = Builder("") + .withTable(tableName) + + /** + * @param name The raw name of this alias. + * @return A new instance without adding identifier `` to any part of the query. + */ + fun rawBuilder(name: String): Builder { + return Builder(name) + .shouldStripIdentifier(false) + .shouldAddIdentifierToName(false) + } + + fun of(name: String): NameAlias = builder(name).build() + + fun of(name: String, aliasName: String): NameAlias = + builder(name).`as`(aliasName).build() + + fun ofTable(tableName: String, name: String): NameAlias = + builder(name).withTable(tableName).build() + } +} + +val String.nameAlias + get() = NameAlias.of(this) + +fun String.`as`(alias: String = "") = NameAlias.of(this, alias) diff --git a/lib/src/main/kotlin/com/dbflow5/query/Operator.kt b/lib/src/main/kotlin/com/dbflow5/query/Operator.kt new file mode 100644 index 000000000..157a33f54 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Operator.kt @@ -0,0 +1,649 @@ +@file:Suppress("UNCHECKED_CAST") + +package com.dbflow5.query + +import com.dbflow5.annotation.Collate +import com.dbflow5.appendOptional +import com.dbflow5.config.FlowLog +import com.dbflow5.config.FlowManager +import com.dbflow5.converter.TypeConverter +import com.dbflow5.query.property.Property +import com.dbflow5.sql.Query + +/** + * Description: The class that contains a column name, Operator, and value. + * This class is mostly reserved for internal use at this point. Using this class directly should be avoided + * and use the generated [Property] instead. + */ +class Operator : BaseOperator, IOperator { + + private var typeConverter: TypeConverter<*, *>? = null + private var convertToDB: Boolean = false + + private var convertToString = true + + override val query: String + get() = appendToQuery() + + /** + * Creates a new instance + * + * @param [nameAlias] The name of the column in the DB + */ + internal constructor(nameAlias: NameAlias) : super(nameAlias) + + internal constructor(alias: NameAlias, typeConverter: TypeConverter<*, *>, convertToDB: Boolean) : super(alias) { + this.typeConverter = typeConverter + this.convertToDB = convertToDB + } + + internal constructor(operator: Operator<*>) : super(operator.nameAlias) { + this.typeConverter = operator.typeConverter + this.convertToDB = operator.convertToDB + this.value = operator.value + } + + override fun appendConditionToQuery(queryBuilder: StringBuilder) { + queryBuilder.append(columnName()).append(operation()) + + // Do not use value for certain operators + // If is raw, we do not want to convert the value to a string. + if (isValueSet) { + queryBuilder.append(if (convertToString) convertObjectToString(value(), true) else value()) + } + + postArgument()?.let { queryBuilder.append(" $it") } + } + + override fun isNull() = apply { + operation = " ${Operation.IS_NULL} " + } + + override fun isNotNull() = apply { + operation = " ${Operation.IS_NOT_NULL} " + } + + override fun `is`(value: T?): Operator { + operation = Operation.EQUALS + return value(value) + } + + override fun eq(value: T?): Operator = `is`(value) + + override fun isNot(value: T?): Operator { + operation = Operation.NOT_EQUALS + return value(value) + } + + override fun notEq(value: T?): Operator = isNot(value) + + /** + * Uses the LIKE operation. Case insensitive comparisons. + * + * @param value Uses sqlite LIKE regex to match rows. + * It must be a string to escape it properly. + * There are two wildcards: % and _ + * % represents [0,many) numbers or characters. + * The _ represents a single number or character. + * @return This condition + */ + override fun like(value: String): Operator { + operation = " ${Operation.LIKE} " + return value(value) + } + + /** + * Uses the NOT LIKE operation. Case insensitive comparisons. + * + * @param value Uses sqlite LIKE regex to inversely match rows. + * It must be a string to escape it properly. + * There are two wildcards: % and _ + * % represents [0,many) numbers or characters. + * The _ represents a single number or character. + * @return This condition + */ + override fun notLike(value: String): Operator { + operation = " ${Operation.NOT_LIKE} " + return value(value) + } + + /** + * Uses the GLOB operation. Similar to LIKE except it uses case sensitive comparisons. + * + * @param value Uses sqlite GLOB regex to match rows. + * It must be a string to escape it properly. + * There are two wildcards: * and ? + * * represents [0,many) numbers or characters. + * The ? represents a single number or character + * @return This condition + */ + override fun glob(value: String): Operator { + operation = " ${Operation.GLOB} " + return value(value) + } + + /** + * The value of the parameter + * + * @param value The value of the column in the DB + * @return This condition + */ + fun value(value: Any?) = apply { + this.value = value + isValueSet = true + } + + override fun greaterThan(value: T): Operator { + operation = Operation.GREATER_THAN + return value(value) + } + + override fun greaterThanOrEq(value: T): Operator { + operation = Operation.GREATER_THAN_OR_EQUALS + return value(value) + } + + override fun lessThan(value: T): Operator { + operation = Operation.LESS_THAN + return value(value) + } + + override fun lessThanOrEq(value: T): Operator { + operation = Operation.LESS_THAN_OR_EQUALS + return value(value) + } + + override fun plus(value: T): Operator = assignValueOp(value, Operation.PLUS) + + override fun minus(value: T): Operator = assignValueOp(value, Operation.MINUS) + + override fun div(value: T): Operator = assignValueOp(value, Operation.DIVISION) + + override fun times(value: T): Operator = assignValueOp(value, Operation.MULTIPLY) + + override fun rem(value: T): Operator = assignValueOp(value, Operation.MOD) + + /** + * Add a custom operation to this argument + * + * @param operation The SQLite operator + * @return This condition + */ + fun operation(operation: String) = apply { + this.operation = operation + } + + /** + * Adds a COLLATE to the end of this condition + * + * @param collation The SQLite collate function + * @return This condition. + */ + infix fun collate(collation: String) = apply { + postArg = "COLLATE $collation" + } + + /** + * Adds a COLLATE to the end of this condition using the [Collate] enum. + * + * @param collation The SQLite collate function + * @return This condition. + */ + infix fun collate(collation: Collate) = apply { + if (collation == Collate.NONE) { + postArg = null + } else { + collate(collation.name) + } + } + + /** + * Appends an optional SQL string to the end of this condition + */ + infix fun postfix(postfix: String) = apply { + postArg = postfix + } + + /** + * Optional separator when chaining this Operator within a [OperatorGroup] + * + * @param separator The separator to use + * @return This instance + */ + override fun separator(separator: String) = apply { + this.separator = separator + } + + override fun `is`(conditional: IConditional): Operator<*> = + assignValueOp(conditional, Operation.EQUALS) + + override fun eq(conditional: IConditional): Operator<*> = + assignValueOp(conditional, Operation.EQUALS) + + override fun isNot(conditional: IConditional): Operator<*> = + assignValueOp(conditional, Operation.NOT_EQUALS) + + override fun notEq(conditional: IConditional): Operator<*> = + assignValueOp(conditional, Operation.NOT_EQUALS) + + override fun like(conditional: IConditional): Operator = like(conditional.query) + + override fun glob(conditional: IConditional): Operator = glob(conditional.query) + + override fun greaterThan(conditional: IConditional): Operator = + assignValueOp(conditional, Operation.GREATER_THAN) + + override fun greaterThanOrEq(conditional: IConditional): Operator = + assignValueOp(conditional, Operation.GREATER_THAN_OR_EQUALS) + + override fun lessThan(conditional: IConditional): Operator = + assignValueOp(conditional, Operation.LESS_THAN) + + override fun lessThanOrEq(conditional: IConditional): Operator = + assignValueOp(conditional, Operation.LESS_THAN_OR_EQUALS) + + override fun between(conditional: IConditional): Between<*> = Between(this as Operator, conditional) + + override fun `in`(firstConditional: IConditional, vararg conditionals: IConditional): In<*> = + In(this as Operator, firstConditional, true, *conditionals) + + override fun notIn(firstConditional: IConditional, vararg conditionals: IConditional): In<*> = + In(this as Operator, firstConditional, false, *conditionals) + + override fun notIn(firstBaseModelQueriable: BaseModelQueriable<*>, + vararg baseModelQueriables: BaseModelQueriable<*>): In<*> = + In(this as Operator, firstBaseModelQueriable, false, *baseModelQueriables) + + override fun `is`(baseModelQueriable: BaseModelQueriable<*>): Operator<*> = + assignValueOp(baseModelQueriable, Operation.EQUALS) + + override fun eq(baseModelQueriable: BaseModelQueriable<*>): Operator<*> = + assignValueOp(baseModelQueriable, Operation.EQUALS) + + override fun isNot(baseModelQueriable: BaseModelQueriable<*>): Operator<*> = + assignValueOp(baseModelQueriable, Operation.NOT_EQUALS) + + override fun notEq(baseModelQueriable: BaseModelQueriable<*>): Operator<*> = + assignValueOp(baseModelQueriable, Operation.NOT_EQUALS) + + override fun like(baseModelQueriable: BaseModelQueriable<*>): Operator = + assignValueOp(baseModelQueriable, Operation.LIKE) + + override fun notLike(conditional: IConditional): Operator<*> = + assignValueOp(conditional, Operation.NOT_LIKE) + + override fun notLike(baseModelQueriable: BaseModelQueriable<*>): Operator<*> = + assignValueOp(baseModelQueriable, Operation.NOT_LIKE) + + override fun glob(baseModelQueriable: BaseModelQueriable<*>): Operator = + assignValueOp(baseModelQueriable, Operation.GLOB) + + override fun greaterThan(baseModelQueriable: BaseModelQueriable<*>): Operator = + assignValueOp(baseModelQueriable, Operation.GREATER_THAN) + + override fun greaterThanOrEq(baseModelQueriable: BaseModelQueriable<*>): Operator = + assignValueOp(baseModelQueriable, Operation.GREATER_THAN_OR_EQUALS) + + override fun lessThan(baseModelQueriable: BaseModelQueriable<*>): Operator = + assignValueOp(baseModelQueriable, Operation.LESS_THAN) + + override fun lessThanOrEq(baseModelQueriable: BaseModelQueriable<*>): Operator = + assignValueOp(baseModelQueriable, Operation.LESS_THAN_OR_EQUALS) + + operator fun plus(value: IConditional): Operator<*> = assignValueOp(value, Operation.PLUS) + + operator fun minus(value: IConditional): Operator<*> = assignValueOp(value, Operation.MINUS) + + operator fun div(value: IConditional): Operator<*> = assignValueOp(value, Operation.DIVISION) + + operator fun times(value: IConditional): Operator<*> = assignValueOp(value, Operation.MULTIPLY) + + operator fun rem(value: IConditional): Operator<*> = assignValueOp(value, Operation.MOD) + + override fun plus(value: BaseModelQueriable<*>): Operator<*> = + assignValueOp(value, Operation.PLUS) + + override fun minus(value: BaseModelQueriable<*>): Operator<*> = + assignValueOp(value, Operation.MINUS) + + override fun div(value: BaseModelQueriable<*>): Operator<*> = + assignValueOp(value, Operation.DIVISION) + + override fun times(value: BaseModelQueriable<*>): Operator<*> = + assignValueOp(value, Operation.MULTIPLY) + + override fun rem(value: BaseModelQueriable<*>): Operator<*> = + assignValueOp(value, Operation.MOD) + + override fun between(baseModelQueriable: BaseModelQueriable<*>): Between<*> = + Between(this as Operator, baseModelQueriable) + + override fun `in`(firstBaseModelQueriable: BaseModelQueriable<*>, vararg baseModelQueriables: BaseModelQueriable<*>): In<*> = + In(this as Operator, firstBaseModelQueriable, true, *baseModelQueriables) + + override fun concatenate(value: Any?): Operator { + var _value = value + operation = "${Operation.EQUALS}${columnName()}" + + var typeConverter: TypeConverter<*, Any>? = this.typeConverter as TypeConverter<*, Any>? + if (typeConverter == null && _value != null) { + typeConverter = FlowManager.getTypeConverterForClass(_value.javaClass) as TypeConverter<*, Any>? + } + if (typeConverter != null && convertToDB) { + _value = typeConverter.getDBValue(_value) + } + operation = when (_value) { + is String, is IOperator<*>, is Char -> "$operation ${Operation.CONCATENATE} " + is Number -> "$operation ${Operation.PLUS} " + else -> throw IllegalArgumentException( + "Cannot concatenate the ${if (_value != null) _value.javaClass else "null"}") + } + this.value = _value + isValueSet = true + return this + } + + override fun concatenate(conditional: IConditional): Operator = + concatenate(conditional as Any) + + /** + * Turns this condition into a SQL BETWEEN operation + * + * @param value The value of the first argument of the BETWEEN clause + * @return Between operator + */ + override fun between(value: T): Between = Between(this, value) + + @SafeVarargs + override fun `in`(firstValue: T, vararg values: T): In = + In(this, firstValue, true, *values) + + @SafeVarargs + override fun notIn(firstValue: T, vararg values: T): In = + In(this, firstValue, false, *values) + + override fun `in`(values: Collection): In = In(this, values, true) + + override fun notIn(values: Collection): In = In(this, values, false) + + override fun convertObjectToString(obj: Any?, appendInnerParenthesis: Boolean): String? = + (typeConverter as? TypeConverter<*, Any>?)?.let { typeConverter -> + var converted = obj + try { + converted = if (convertToDB) typeConverter.getDBValue(obj) else obj + } catch (c: ClassCastException) { + // if object type is not valid converted type, just use type as is here. + // if object type is not valid converted type, just use type as is here. + FlowLog.log(FlowLog.Level.I, "Value passed to operation is not valid type" + + " for TypeConverter in the column. Preserving value $obj to be used as is.") + } + + convertValueToString(converted, appendInnerParenthesis, false) + } ?: super.convertObjectToString(obj, appendInnerParenthesis) + + private fun assignValueOp(value: Any?, operation: String): Operator { + return if (!isValueSet) { + this.operation = operation + value(value) + } else { + convertToString = false + // convert value to a string value because of conversion. + value(convertValueToString(this.value) + operation + convertValueToString(value)) + } + } + + /** + * Static constants that define condition operations + */ + object Operation { + + /** + * Equals comparison + */ + const val EQUALS = "=" + + /** + * Not-equals comparison + */ + const val NOT_EQUALS = "!=" + + /** + * String concatenation + */ + const val CONCATENATE = "||" + + /** + * Number addition + */ + const val PLUS = "+" + + /** + * Number subtraction + */ + const val MINUS = "-" + + const val DIVISION = "/" + + const val MULTIPLY = "*" + + const val MOD = "%" + + /** + * If something is LIKE another (a case insensitive search). + * There are two wildcards: % and _ + * % represents [0,many) numbers or characters. + * The _ represents a single number or character. + */ + const val LIKE = "LIKE" + + /** + * If something is NOT LIKE another (a case insensitive search). + * There are two wildcards: % and _ + * % represents [0,many) numbers or characters. + * The _ represents a single number or character. + */ + const val NOT_LIKE = "NOT LIKE" + + /** + * If something is case sensitive like another. + * It must be a string to escape it properly. + * There are two wildcards: * and ? + * * represents [0,many) numbers or characters. + * The ? represents a single number or character + */ + const val GLOB = "GLOB" + + /** + * Greater than some value comparison + */ + const val GREATER_THAN = ">" + + /** + * Greater than or equals to some value comparison + */ + const val GREATER_THAN_OR_EQUALS = ">=" + + /** + * Less than some value comparison + */ + const val LESS_THAN = "<" + + /** + * Less than or equals to some value comparison + */ + const val LESS_THAN_OR_EQUALS = "<=" + + /** + * Between comparison. A simplification of X<Y AND Y<Z to Y BETWEEN X AND Z + */ + const val BETWEEN = "BETWEEN" + + /** + * AND comparison separator + */ + const val AND = "AND" + + /** + * OR comparison separator + */ + const val OR = "OR" + + /** + * An empty value for the condition. + */ + const val EMPTY_PARAM = "?" + + /** + * Special operation that specify if the column is not null for a specified row. Use of this as + * an operator will ignore the value of the [Operator] for it. + */ + const val IS_NOT_NULL = "IS NOT NULL" + + /** + * Special operation that specify if the column is null for a specified row. Use of this as + * an operator will ignore the value of the [Operator] for it. + */ + const val IS_NULL = "IS NULL" + + /** + * The SQLite IN command that will select rows that are contained in a list of values. + * EX: SELECT * from Table where column IN ('first', 'second', etc) + */ + const val IN = "IN" + + /** + * The reverse of the [.IN] command that selects rows that are not contained + * in a list of values specified. + */ + const val NOT_IN = "NOT IN" + } + + /** + * The SQL BETWEEN operator that contains two values instead of the normal 1. + */ + class Between + /** + * Creates a new instance + * + * @param operator + * @param value The value of the first argument of the BETWEEN clause + */ + internal constructor(operator: Operator, value: T) : BaseOperator(operator.nameAlias), Query { + + private var secondValue: T? = null + + override val query: String + get() = appendToQuery() + + init { + this.operation = " ${Operation.BETWEEN} " + this.value = value + isValueSet = true + this.postArg = operator.postArgument() + } + + infix fun and(secondValue: T?) = apply { + this.secondValue = secondValue + } + + fun secondValue(): T? = secondValue + + override fun appendConditionToQuery(queryBuilder: StringBuilder) { + queryBuilder.append(columnName()).append(operation()) + .append(convertObjectToString(value(), true)) + .append(" ${Operation.AND} ") + .append(convertObjectToString(secondValue(), true)) + .append(" ") + .appendOptional(postArgument()) + } + } + + /** + * The SQL IN and NOT IN operator that specifies a list of values to SELECT rows from. + * EX: SELECT * FROM myTable WHERE columnName IN ('column1','column2','etc') + */ + class In : BaseOperator, Query { + + private val inArguments = arrayListOf() + + override val query: String + get() = appendToQuery() + + /** + * Creates a new instance + * + * @param operator The operator object to pass in. We only use the column name here. + * @param firstArgument The first value in the IN query as one is required. + * @param isIn if this is an [Operator.Operation.IN] + * statement or a [Operator.Operation.NOT_IN] + */ + @SafeVarargs + internal constructor(operator: Operator, firstArgument: T?, isIn: Boolean, vararg arguments: T?) : super(operator.columnAlias()) { + inArguments.add(firstArgument) + inArguments.addAll(arguments) + operation = " ${if (isIn) Operation.IN else Operation.NOT_IN} " + } + + internal constructor(operator: Operator, args: Collection, isIn: Boolean) : super(operator.columnAlias()) { + inArguments.addAll(args) + operation = " ${if (isIn) Operation.IN else Operation.NOT_IN} " + } + + /** + * Appends another value to this In statement + * + * @param argument The non-type converted value of the object. The value will be converted + * in a [OperatorGroup]. + * @return + */ + infix fun and(argument: T?): In { + inArguments.add(argument) + return this + } + + override fun appendConditionToQuery(queryBuilder: StringBuilder) { + queryBuilder.append(columnName()) + .append(operation()) + .append("(") + .append(joinArguments(",", inArguments, this)) + .append(")") + } + } + + companion object { + + @JvmStatic + fun convertValueToString(value: Any?): String? = + convertValueToString(value, false) + + @JvmStatic + fun op(column: NameAlias): Operator = Operator(column) + + @JvmStatic + fun op(alias: NameAlias, typeConverter: TypeConverter<*, *>, convertToDB: Boolean): Operator = + Operator(alias, typeConverter, convertToDB) + } + +} + +fun NameAlias.op() = Operator.op(this) + +fun String.op(): Operator = nameAlias.op() + +infix fun Operator.and(sqlOperator: SQLOperator): OperatorGroup = OperatorGroup.clause(this).and(sqlOperator) + +infix fun Operator.or(sqlOperator: SQLOperator): OperatorGroup = OperatorGroup.clause(this).or(sqlOperator) + +infix fun Operator.andAll(sqlOperator: Collection): OperatorGroup = OperatorGroup.clause(this).andAll(sqlOperator) + +infix fun Operator.orAll(sqlOperator: Collection): OperatorGroup = OperatorGroup.clause(this).orAll(sqlOperator) + +infix fun Operator.`in`(values: Array): Operator.In = when (values.size) { + 1 -> `in`(values[0]) + else -> this.`in`(values[0], *values.sliceArray(IntRange(1, values.size))) +} + +infix fun Operator.notIn(values: Array): Operator.In = when (values.size) { + 1 -> notIn(values[0]) + else -> this.notIn(values[0], *values.sliceArray(IntRange(1, values.size))) +} \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/OperatorGroup.kt b/lib/src/main/kotlin/com/dbflow5/query/OperatorGroup.kt new file mode 100644 index 000000000..10b6b6686 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/OperatorGroup.kt @@ -0,0 +1,196 @@ +package com.dbflow5.query + +import com.dbflow5.query.Operator.Operation +import com.dbflow5.sql.Query + +/** + * Allows combining of [SQLOperator] into one condition. + */ +class OperatorGroup +@JvmOverloads +constructor(columnName: NameAlias? = null) : BaseOperator(columnName), Query, Iterable { + + private val conditionsList = arrayListOf() + + private var internalQuery: String? = null + private var isChanged: Boolean = false + private var allCommaSeparated: Boolean = false + private var useParenthesis = true + + val conditions: List + get() = conditionsList + + private val querySafe: String + get() = appendToQuery() + + init { + + // default is AND + separator = Operation.AND + } + + /** + * Will ignore all separators for the group and make them separated by comma. This is useful + * in [Set] statements. + * + * @param allCommaSeparated All become comma separated. + * @return This instance. + */ + fun setAllCommaSeparated(allCommaSeparated: Boolean) = apply { + this.allCommaSeparated = allCommaSeparated + isChanged = true + } + + /** + * Sets whether we use paranthesis when grouping this within other [SQLOperator]. The default + * is true, but if no conditions exist there are no paranthesis anyways. + * + * @param useParenthesis true if we use them, false if not. + */ + fun setUseParenthesis(useParenthesis: Boolean) = apply { + this.useParenthesis = useParenthesis + isChanged = true + } + + /** + * Appends the [SQLOperator] with an [Operation.OR] + * + * @param sqlOperator The condition to append. + * @return This instance. + */ + fun or(sqlOperator: SQLOperator): OperatorGroup = operator(Operation.OR, sqlOperator) + + /** + * Appends the [SQLOperator] with an [Operation.AND] + */ + fun and(sqlOperator: SQLOperator): OperatorGroup = operator(Operation.AND, sqlOperator) + + /** + * Applies the [Operation.AND] to all of the passed + * [SQLOperator]. + */ + fun andAll(vararg sqlOperators: SQLOperator) = apply { + sqlOperators.forEach { and(it) } + } + + /** + * Applies the [Operation.AND] to all of the passed + * [SQLOperator]. + */ + fun andAll(sqlOperators: Collection) = apply { + sqlOperators.forEach { and(it) } + } + + /** + * Applies the [Operation.AND] to all of the passed + * [SQLOperator]. + */ + fun orAll(vararg sqlOperators: SQLOperator) = apply { + sqlOperators.forEach { or(it) } + } + + /** + * Applies the [Operation.AND] to all of the passed + * [SQLOperator]. + */ + fun orAll(sqlOperators: Collection) = apply { + sqlOperators.forEach { or(it) } + } + + /** + * Appends the [SQLOperator] with the specified operator string. + */ + fun operator(operator: String, sqlOperator: SQLOperator?) = apply { + if (sqlOperator != null) { + setPreviousSeparator(operator) + conditionsList.add(sqlOperator) + isChanged = true + } + } + + override fun appendConditionToQuery(queryBuilder: StringBuilder) { + val conditionListSize = conditionsList.size + if (useParenthesis && conditionListSize > 0) { + queryBuilder.append("(") + } + for (i in 0 until conditionListSize) { + val condition = conditionsList[i] + condition.appendConditionToQuery(queryBuilder) + if (!allCommaSeparated && condition.hasSeparator() && i < conditionListSize - 1) { + queryBuilder.append(" ${condition.separator()} ") + } else if (i < conditionListSize - 1) { + queryBuilder.append(", ") + } + } + if (useParenthesis && conditionListSize > 0) { + queryBuilder.append(")") + } + } + + /** + * Sets the last condition to use the separator specified + * + * @param separator AND, OR, etc. + */ + private fun setPreviousSeparator(separator: String) { + if (conditionsList.size > 0) { + // set previous to use OR separator + conditionsList[conditionsList.size - 1].separator(separator) + } + } + + override val query: String + get() { + if (isChanged) { + internalQuery = querySafe + } + return internalQuery ?: "" + } + + override fun toString(): String = querySafe + + val size: Int + get() = conditionsList.size + + override fun iterator(): Iterator = conditionsList.iterator() + + companion object { + + /** + * @return Starts an arbitrary clause of conditions to use. + */ + @JvmStatic + fun clause(): OperatorGroup = OperatorGroup() + + /** + * @return Starts an arbitrary clause of conditions to use with first param as conditions separated by AND. + */ + @JvmStatic + fun clause(vararg condition: SQLOperator): OperatorGroup = + OperatorGroup().andAll(*condition) + + /** + * @return Starts an arbitrary clause of conditions to use, that when included in other [SQLOperator], + * does not append parenthesis to group it. + */ + @JvmStatic + fun nonGroupingClause(): OperatorGroup = OperatorGroup().setUseParenthesis(false) + + /** + * @return Starts an arbitrary clause of conditions (without parenthesis) to use with first param as conditions separated by AND. + */ + @JvmStatic + fun nonGroupingClause(vararg condition: SQLOperator): OperatorGroup = + OperatorGroup().setUseParenthesis(false).andAll(*condition) + } +} + +fun SQLOperator.clause() = OperatorGroup.clause(this) + +infix fun OperatorGroup.and(sqlOperator: SQLOperator) = and(sqlOperator) + +infix fun OperatorGroup.or(sqlOperator: SQLOperator) = or(sqlOperator) + +infix fun OperatorGroup.and(sqlOperator: OperatorGroup) = clause().and(sqlOperator) + +infix fun OperatorGroup.or(sqlOperator: OperatorGroup) = clause().or(sqlOperator) \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/OrderBy.kt b/lib/src/main/kotlin/com/dbflow5/query/OrderBy.kt new file mode 100644 index 000000000..6532176b2 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/OrderBy.kt @@ -0,0 +1,76 @@ +package com.dbflow5.query + +import com.dbflow5.annotation.Collate +import com.dbflow5.query.property.IProperty +import com.dbflow5.sql.Query + +/** + * Description: Class that represents a SQL order-by. + */ +class OrderBy +@JvmOverloads +constructor(private val column: NameAlias? = null, + private var isAscending: Boolean = false) : Query { + + private var collation: Collate? = null + private var orderByString: String? + + override val query: String + get() { + val locOrderByString = orderByString + return if (locOrderByString == null) { + val query = StringBuilder() + .append(column) + .append(" ") + if (collation != null) { + query.append("COLLATE $collation ") + } + query.append(if (isAscending) ASCENDING else DESCENDING) + query.toString() + } else { + locOrderByString + } + } + + internal constructor(orderByString: String) : this(column = null) { + this.orderByString = orderByString + } + + init { + this.orderByString = null + } + + fun ascending() = apply { + isAscending = true + } + + fun descending() = apply { + isAscending = false + } + + fun collate(collate: Collate) = apply { + this.collation = collate + } + + override fun toString(): String = query + + companion object { + + @JvmField + val ASCENDING = "ASC" + + @JvmField + val DESCENDING = "DESC" + + @JvmStatic + fun fromProperty(property: IProperty<*>): OrderBy = OrderBy(property.nameAlias) + + @JvmStatic + fun fromNameAlias(nameAlias: NameAlias): OrderBy = OrderBy(nameAlias) + + @JvmStatic + fun fromString(orderByString: String): OrderBy = OrderBy(orderByString) + } +} + +infix fun OrderBy.collate(collate: Collate) = collate(collate) \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/Queriable.kt b/lib/src/main/kotlin/com/dbflow5/query/Queriable.kt new file mode 100644 index 000000000..9f472e7af --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Queriable.kt @@ -0,0 +1,66 @@ +package com.dbflow5.query + +import com.dbflow5.config.databaseForTable +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.database.DatabaseWrapper +import com.dbflow5.database.FlowCursor +import com.dbflow5.sql.Query +import com.dbflow5.structure.ChangeAction +import com.dbflow5.structure.Model + +/** + * Description: The most basic interface that some of the classes such as [Insert], [ModelQueriable], + * [Set], and more implement for convenience. + */ +interface Queriable : Query { + + val primaryAction: ChangeAction + + /** + * @return A cursor from the DB based on this query + */ + fun cursor(databaseWrapper: DatabaseWrapper): FlowCursor? + + /** + * @return A new [DatabaseStatement] from this query. + */ + fun compileStatement(databaseWrapper: DatabaseWrapper): DatabaseStatement + + /** + * @return the long value of the results of query. + */ + fun longValue(databaseWrapper: DatabaseWrapper): Long + + /** + * @return This may return the number of rows affected from a [Set] or [Delete] statement. + * If not, returns [Model.INVALID_ROW_ID] + */ + fun executeUpdateDelete(databaseWrapper: DatabaseWrapper): Long + + /** + * @return This may return the number of rows affected from a [Insert] statement. + * If not, returns [Model.INVALID_ROW_ID] + */ + fun executeInsert(databaseWrapper: DatabaseWrapper): Long + + /** + * @return True if this query has data. It will run a [.count] greater than 0. + */ + fun hasData(databaseWrapper: DatabaseWrapper): Boolean + + /** + * Will not return a result, rather simply will execute a SQL statement. Use this for non-SELECT statements or when + * you're not interested in the result. + */ + fun execute(databaseWrapper: DatabaseWrapper) + +} + +inline val BaseQueriable.cursor + get() = cursor(databaseForTable()) + +inline val BaseQueriable.hasData + get() = hasData(databaseForTable()) + +inline val BaseQueriable.statement + get() = compileStatement(databaseForTable()) diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/SQLOperator.java b/lib/src/main/kotlin/com/dbflow5/query/SQLOperator.kt similarity index 50% rename from dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/SQLOperator.java rename to lib/src/main/kotlin/com/dbflow5/query/SQLOperator.kt index 22fb92b5b..4a551a631 100644 --- a/dbflow/src/main/java/com/raizlabs/android/dbflow/sql/language/SQLOperator.java +++ b/lib/src/main/kotlin/com/dbflow5/query/SQLOperator.kt @@ -1,37 +1,30 @@ -package com.raizlabs.android.dbflow.sql.language; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.raizlabs.android.dbflow.sql.QueryBuilder; +package com.dbflow5.query /** * Description: Basic interface for all of the Operator classes. */ -public interface SQLOperator { +interface SQLOperator { /** - * Appends itself to the {@link QueryBuilder} + * Appends itself to the [StringBuilder] * * @param queryBuilder The builder to append to. */ - void appendConditionToQuery(@NonNull QueryBuilder queryBuilder); + fun appendConditionToQuery(queryBuilder: StringBuilder) /** * The name of the column. * * @return The column name. */ - @NonNull - String columnName(); + fun columnName(): String /** - * The separator for this condition when paired with a {@link OperatorGroup} + * The separator for this condition when paired with a [OperatorGroup] * * @return The separator, an AND, OR, or other kinds. */ - @Nullable - String separator(); + fun separator(): String? /** * Sets the separator for this condition @@ -39,24 +32,27 @@ public interface SQLOperator { * @param separator The string AND, OR, or something else. * @return This instance. */ - @NonNull - SQLOperator separator(@NonNull String separator); + fun separator(separator: String): SQLOperator /** * @return true if it has a separator, false if not. */ - boolean hasSeparator(); + fun hasSeparator(): Boolean /** * @return the operation that is used. */ - @NonNull - String operation(); + fun operation(): String /** * @return The raw value of the condition. */ - @Nullable - Object value(); + fun value(): Any? } + +fun SQLOperator.appendToQuery(): String { + val queryBuilder = StringBuilder() + appendConditionToQuery(queryBuilder) + return queryBuilder.toString() +} \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/SQLite.kt b/lib/src/main/kotlin/com/dbflow5/query/SQLite.kt new file mode 100644 index 000000000..fa85d1246 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/SQLite.kt @@ -0,0 +1,138 @@ +@file:JvmName("SQLite") + + +package com.dbflow5.query + +import com.dbflow5.query.property.IProperty +import com.dbflow5.query.property.Property +import com.dbflow5.structure.Model +import kotlin.reflect.KClass + + +/** + * @param properties The properties/columns to SELECT. + * @return A beginning of the SELECT statement. + */ +fun select(vararg properties: IProperty<*>): Select = Select(*properties) + +/** + * Starts a new SELECT COUNT(property1, property2, propertyn) (if properties specified) or + * SELECT COUNT(*). + * + * @param properties Optional, if specified returns the count of non-null ROWs from a specific single/group of columns. + * @return A new select statement SELECT COUNT(expression) + */ +fun selectCountOf(vararg properties: IProperty<*>): Select = Select(count(*properties)) + +inline fun update() = update(T::class.java) + +/** + * @param table The tablet to update. + * @return A new UPDATE statement. + */ +fun update(table: Class): Update = Update(table) + +/** + * @param table The table to update. + * @return A new UPDATE statement. + */ +fun update(table: KClass) = update(table.java) + +inline fun insertInto() = insert(columns = *arrayOf()) + +inline fun insert(vararg columns: Property<*>) = insert(T::class, *columns) + +inline fun insert(vararg columnValues: Pair, Any?>) = insert(T::class) + .columnValues(*columnValues) + +inline fun insert(vararg operators: SQLOperator) = insert(T::class) + .columnValues(*operators) + +/** + * @param table The table to insert. + * @return A new INSERT statement. + */ +fun insert(table: Class, vararg columns: Property<*>): Insert = Insert(table, *columns) + +/** + * @param table The table to insert. + * @return A new INSERT statement. + */ +fun insert(table: KClass, vararg columns: Property<*>) = insert(table.java, *columns) + +/** + * @return Begins a DELETE statement. + */ +fun delete(): Delete = Delete() + +inline fun delete() = delete(T::class.java) + +/** + * Starts a DELETE statement on the specified table. + * + * @param table The table to delete from. + * @param [T] The class that implements [Model]. + * @return A [From] with specified DELETE on table. + */ +fun delete(table: Class): From = delete().from(table) + +/** + * Starts an INDEX statement on specified table. + * + * @param name The name of the index. + * @param [T] The class that implements [Model]. + * @return A new INDEX statement. + */ +fun index(name: String, table: Class): Index = Index(name, table) + + +/** + * Starts an INDEX statement on specified table. + * + * @param name The name of the index. + * @param [T] The class that implements [Model]. + * @return A new INDEX statement. + */ +fun index(name: String, table: KClass): Index = Index(name, table.java) + +/** + * Starts a TRIGGER statement. + * + * @param name The name of the trigger. + * @return A new TRIGGER statement. + */ +fun createTrigger(name: String): Trigger = Trigger.create(name) + +/** + * Starts a temporary TRIGGER statement. + * + * @param name The name of the trigger. + * @return A new TEMPORARY TRIGGER statement. + */ +fun createTempTrigger(name: String): Trigger = Trigger.create(name).temporary() + +/** + * Starts a CASE statement. + * + * @param operator The condition to check for in the WHEN. + * @return A new [CaseCondition]. + */ +fun caseWhen(operator: SQLOperator): CaseCondition = Case().whenever(operator) + +/** + * Starts an efficient CASE statement. The value passed here is only evaulated once. A non-efficient + * case statement will evaluate all of its [SQLOperator]. + * + * @param caseColumn The value + */ +@JvmName("_case") +fun case(caseColumn: Property): Case = Case(caseColumn) + +/** + * Starts an efficient CASE statement. The value passed here is only evaulated once. A non-efficient + * case statement will evaluate all of its [SQLOperator]. + * + * @param caseColumn The value + */ +@JvmName("_case") +fun case(caseColumn: IProperty<*>): Case = Case(caseColumn) \ No newline at end of file diff --git a/lib/src/main/kotlin/com/dbflow5/query/SQLiteStatementListener.kt b/lib/src/main/kotlin/com/dbflow5/query/SQLiteStatementListener.kt new file mode 100644 index 000000000..6a74d9742 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/SQLiteStatementListener.kt @@ -0,0 +1,31 @@ +package com.dbflow5.query + +import com.dbflow5.adapter.InternalAdapter +import com.dbflow5.adapter.ModelAdapter +import com.dbflow5.database.DatabaseStatement +import com.dbflow5.structure.Model + +/** + * Description: Marks a [Model] as subscribing to the [DatabaseStatement] + * that is used to [Model.insert] a model into the DB. + */ +interface SQLiteStatementListener { + + /** + * Called at the end of [InternalAdapter.bindToInsertStatement] + * Perform a custom manipulation of the statement as willed. + * + * @param databaseStatement The insert statement from the [ModelAdapter] + */ + fun onBindToInsertStatement(databaseStatement: DatabaseStatement) + + /** + * Called at the end of [InternalAdapter.bindToUpdateStatement] + * Perform a custom manipulation of the statement as willed. + * + * @param databaseStatement The insert statement from the [ModelAdapter] + */ + fun onBindToUpdateStatement(databaseStatement: DatabaseStatement) + + fun onBindToDeleteStatement(databaseStatement: DatabaseStatement) +} diff --git a/lib/src/main/kotlin/com/dbflow5/query/Select.kt b/lib/src/main/kotlin/com/dbflow5/query/Select.kt new file mode 100644 index 000000000..e88d1f106 --- /dev/null +++ b/lib/src/main/kotlin/com/dbflow5/query/Select.kt @@ -0,0 +1,111 @@ +package com.dbflow5.query + +import com.dbflow5.query.property.IProperty +import com.dbflow5.query.property.Property +import com.dbflow5.sql.Query +import com.dbflow5.sql.QueryCloneable +import kotlin.reflect.KClass + +/** + * Description: A SQL SELECT statement generator. It generates the SELECT part of the statement. + */ +class Select +/** + * Creates this instance with the specified columns from the specified [com.dbflow5.config.FlowManager] + * + * @param properties The properties to select from. + */ +internal constructor(vararg properties: IProperty<*>) : Query, QueryCloneable