Repository: greenrobot/greenDAO Branch: master Commit: 0bbb338e17c4 Files: 305 Total size: 1.1 MB Directory structure: gitextract_u02juyft/ ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── DaoCore/ │ ├── .gitignore │ ├── LICENSE │ ├── NOTICE │ ├── build.gradle │ ├── libs/ │ │ └── sqlcipher.jar │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── greenrobot/ │ └── greendao/ │ ├── AbstractDao.java │ ├── AbstractDaoMaster.java │ ├── AbstractDaoSession.java │ ├── DaoException.java │ ├── DaoLog.java │ ├── DbUtils.java │ ├── InternalQueryDaoAccess.java │ ├── InternalUnitTestDaoAccess.java │ ├── Property.java │ ├── async/ │ │ ├── AsyncDaoException.java │ │ ├── AsyncOperation.java │ │ ├── AsyncOperationExecutor.java │ │ ├── AsyncOperationListener.java │ │ └── AsyncSession.java │ ├── database/ │ │ ├── Database.java │ │ ├── DatabaseOpenHelper.java │ │ ├── DatabaseStatement.java │ │ ├── EncryptedDatabase.java │ │ ├── EncryptedDatabaseStatement.java │ │ ├── SqlCipherEncryptedHelper.java │ │ ├── StandardDatabase.java │ │ └── StandardDatabaseStatement.java │ ├── identityscope/ │ │ ├── IdentityScope.java │ │ ├── IdentityScopeLong.java │ │ ├── IdentityScopeObject.java │ │ └── IdentityScopeType.java │ ├── internal/ │ │ ├── DaoConfig.java │ │ ├── FastCursor.java │ │ ├── LongHashMap.java │ │ ├── SqlUtils.java │ │ └── TableStatements.java │ ├── query/ │ │ ├── AbstractQuery.java │ │ ├── AbstractQueryData.java │ │ ├── AbstractQueryWithLimit.java │ │ ├── CloseableListIterator.java │ │ ├── CountQuery.java │ │ ├── CursorQuery.java │ │ ├── DeleteQuery.java │ │ ├── Join.java │ │ ├── LazyList.java │ │ ├── Query.java │ │ ├── QueryBuilder.java │ │ ├── WhereCollector.java │ │ └── WhereCondition.java │ ├── rx/ │ │ ├── RxBase.java │ │ ├── RxDao.java │ │ ├── RxQuery.java │ │ ├── RxTransaction.java │ │ └── RxUtils.java │ └── test/ │ ├── AbstractDaoSessionTest.java │ ├── AbstractDaoTest.java │ ├── AbstractDaoTestLongPk.java │ ├── AbstractDaoTestSinglePk.java │ ├── AbstractDaoTestStringPk.java │ └── DbTest.java ├── DaoGenerator/ │ ├── .freemarker-ide.xml │ ├── .gitignore │ ├── build.gradle │ ├── performance/ │ │ ├── galaxy-nexus.xlsx │ │ └── performance-data.xlsx │ ├── src/ │ │ └── org/ │ │ └── greenrobot/ │ │ └── greendao/ │ │ └── generator/ │ │ ├── ContentProvider.java │ │ ├── DaoGenerator.java │ │ ├── DaoUtil.java │ │ ├── Entity.java │ │ ├── Index.java │ │ ├── Property.java │ │ ├── PropertyOrderList.java │ │ ├── PropertyType.java │ │ ├── Query.java │ │ ├── QueryParam.java │ │ ├── Schema.java │ │ ├── ToMany.java │ │ ├── ToManyBase.java │ │ ├── ToManyWithJoinEntity.java │ │ └── ToOne.java │ ├── src-template/ │ │ ├── content-provider.ftl │ │ ├── dao-deep.ftl │ │ ├── dao-master.ftl │ │ ├── dao-session.ftl │ │ ├── dao-unit-test.ftl │ │ ├── dao.ftl │ │ └── entity.ftl │ └── src-test/ │ └── org/ │ └── greenrobot/ │ └── greendao/ │ └── generator/ │ └── SimpleDaoGeneratorTest.java ├── README.md ├── build.gradle ├── examples/ │ ├── DaoExample/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── greenrobot/ │ │ │ └── greendao/ │ │ │ └── example/ │ │ │ └── NoteTest.java │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── org/ │ │ │ └── greenrobot/ │ │ │ └── greendao/ │ │ │ └── example/ │ │ │ ├── App.java │ │ │ ├── Note.java │ │ │ ├── NoteActivity.java │ │ │ ├── NoteType.java │ │ │ ├── NoteTypeConverter.java │ │ │ └── NotesAdapter.java │ │ └── res/ │ │ ├── layout/ │ │ │ ├── item_note.xml │ │ │ └── main.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── RxDaoExample/ │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── org/ │ │ └── greenrobot/ │ │ └── greendao/ │ │ └── rxexample/ │ │ ├── App.java │ │ ├── MainActivity.java │ │ ├── Note.java │ │ ├── NoteType.java │ │ ├── NoteTypeConverter.java │ │ └── NotesAdapter.java │ └── res/ │ ├── layout/ │ │ ├── activity_main.xml │ │ └── item_note.xml │ └── values/ │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── gradle/ │ ├── publish.gradle │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── greendao-api/ │ ├── LICENSE │ ├── NOTICE │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── greenrobot/ │ └── greendao/ │ ├── annotation/ │ │ ├── Convert.java │ │ ├── Entity.java │ │ ├── Generated.java │ │ ├── Id.java │ │ ├── Index.java │ │ ├── JoinEntity.java │ │ ├── JoinProperty.java │ │ ├── Keep.java │ │ ├── NotNull.java │ │ ├── OrderBy.java │ │ ├── Property.java │ │ ├── ToMany.java │ │ ├── ToOne.java │ │ ├── Transient.java │ │ ├── Unique.java │ │ └── apihint/ │ │ ├── Beta.java │ │ ├── Experimental.java │ │ └── Internal.java │ └── converter/ │ └── PropertyConverter.java ├── javadoc-style/ │ └── stylesheet.css ├── settings.gradle └── tests/ ├── DaoTest/ │ ├── build.gradle │ ├── proguard.cfg │ ├── project.properties │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── org/ │ │ └── greenrobot/ │ │ └── greendao/ │ │ ├── daotest/ │ │ │ ├── DaoSessionConcurrentTest.java │ │ │ ├── DaoSessionConcurrentWALTest.java │ │ │ ├── DaoSessionTest.java │ │ │ ├── DbTestTest.java │ │ │ ├── DbUtilsTest.java │ │ │ ├── DeadlockPreventionTest.java │ │ │ ├── IndexTest.java │ │ │ ├── LongHashMapTest.java │ │ │ ├── async/ │ │ │ │ ├── AbstractAsyncTest.java │ │ │ │ ├── BasicAsyncTest.java │ │ │ │ └── MergeTxAsyncTest.java │ │ │ ├── contentprovider/ │ │ │ │ └── SimpleEntityContentProviderTest.java │ │ │ ├── encrypted/ │ │ │ │ ├── EncryptedDataFileTest.java │ │ │ │ ├── EncryptedDatabaseOpenHelperTest.java │ │ │ │ ├── EncryptedDbUtils.java │ │ │ │ └── EncryptionSimpleEntityTest.java │ │ │ ├── entity/ │ │ │ │ ├── AbcdefEntityTest.java │ │ │ │ ├── AnActiveEntityMultithreadingTest.java │ │ │ │ ├── AnActiveEntityTest.java │ │ │ │ ├── AutoincrementEntityTest.java │ │ │ │ ├── CustomTypeEntityTest.java │ │ │ │ ├── DateEntityTest.java │ │ │ │ ├── ExtendsImplementsEntityTest.java │ │ │ │ ├── IndexedStringEntityTest.java │ │ │ │ ├── JoinManyToDateEntityTest.java │ │ │ │ ├── RelationEntityTest.java │ │ │ │ ├── RelationEntityTestIdentityScope.java │ │ │ │ ├── SimpleEntityNotNullTest.java │ │ │ │ ├── SimpleEntityTest.java │ │ │ │ ├── SpecialNamesEntityTest.java │ │ │ │ ├── SqliteMasterTest.java │ │ │ │ ├── StringKeyValueEntityIdentityScopeTest.java │ │ │ │ ├── StringKeyValueEntityTest.java │ │ │ │ ├── TestEntityIdentityScopeTest.java │ │ │ │ ├── TestEntityTest.java │ │ │ │ ├── TestEntityTestBase.java │ │ │ │ ├── ToManyEntityTest.java │ │ │ │ ├── ToManyTargetEntityTest.java │ │ │ │ ├── TransactionTest.java │ │ │ │ └── TreeEntityTest.java │ │ │ ├── query/ │ │ │ │ ├── CountQueryTest.java │ │ │ │ ├── CountQueryThreadLocalTest.java │ │ │ │ ├── CursorQueryTest.java │ │ │ │ ├── DeleteQueryTest.java │ │ │ │ ├── DeleteQueryThreadLocalTest.java │ │ │ │ ├── JoinTest.java │ │ │ │ ├── LazyListTest.java │ │ │ │ ├── QueryBuilderAndOrTest.java │ │ │ │ ├── QueryBuilderOrderTest.java │ │ │ │ ├── QueryBuilderSimpleTest.java │ │ │ │ ├── QueryForThreadTest.java │ │ │ │ ├── QueryLimitOffsetTest.java │ │ │ │ ├── QuerySpecialNamesTest.java │ │ │ │ └── RawQueryTest.java │ │ │ └── rx/ │ │ │ ├── RxDaoTest.java │ │ │ ├── RxQueryTest.java │ │ │ ├── RxTestHelper.java │ │ │ └── RxTransactionTest.java │ │ └── daotest2/ │ │ └── entity/ │ │ └── KeepEntityTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ └── assets/ │ │ └── minimal-entity.sql │ └── test/ │ └── java/ │ └── org/ │ └── greenrobot/ │ └── greendao/ │ └── unittest/ │ ├── DaoMaster.java │ ├── DaoSession.java │ ├── MinimalEntity.java │ ├── MinimalEntityDao.java │ ├── MinimalEntityTest.java │ └── OptionalDepedenciesTest.java ├── DaoTestBase/ │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── greenrobot/ │ └── greendao/ │ ├── daotest/ │ │ ├── AbcdefEntity.java │ │ ├── AbcdefEntityDao.java │ │ ├── AnActiveEntity.java │ │ ├── AnActiveEntityDao.java │ │ ├── AutoincrementEntity.java │ │ ├── AutoincrementEntityDao.java │ │ ├── CustomTypeEntity.java │ │ ├── CustomTypeEntityDao.java │ │ ├── DaoMaster.java │ │ ├── DaoSession.java │ │ ├── DateEntity.java │ │ ├── DateEntityDao.java │ │ ├── ExtendsImplementsEntity.java │ │ ├── ExtendsImplementsEntityDao.java │ │ ├── IndexedStringEntity.java │ │ ├── IndexedStringEntityDao.java │ │ ├── JoinManyToDateEntity.java │ │ ├── JoinManyToDateEntityDao.java │ │ ├── RelationEntity.java │ │ ├── RelationEntityDao.java │ │ ├── SimpleEntity.java │ │ ├── SimpleEntityContentProvider.java │ │ ├── SimpleEntityDao.java │ │ ├── SimpleEntityNotNull.java │ │ ├── SimpleEntityNotNullDao.java │ │ ├── SpecialNamesEntity.java │ │ ├── SpecialNamesEntityDao.java │ │ ├── SqliteMaster.java │ │ ├── SqliteMasterDao.java │ │ ├── StringKeyValueEntity.java │ │ ├── StringKeyValueEntityDao.java │ │ ├── TestEntity.java │ │ ├── TestEntityDao.java │ │ ├── TestInterface.java │ │ ├── TestSuperclass.java │ │ ├── ToManyEntity.java │ │ ├── ToManyEntityDao.java │ │ ├── ToManyTargetEntity.java │ │ ├── ToManyTargetEntityDao.java │ │ ├── TreeEntity.java │ │ ├── TreeEntityDao.java │ │ ├── customtype/ │ │ │ ├── IntegerListConverter.java │ │ │ ├── MyTimestamp.java │ │ │ └── MyTimestampConverter.java │ │ └── entity/ │ │ └── SimpleEntityNotNullHelper.java │ └── daotest2/ │ ├── KeepEntity.java │ ├── ToManyTarget2.java │ ├── dao/ │ │ ├── DaoMaster.java │ │ ├── DaoSession.java │ │ ├── KeepEntityDao.java │ │ └── ToManyTarget2Dao.java │ ├── specialdao/ │ │ └── RelationSource2Dao.java │ ├── specialentity/ │ │ └── RelationSource2.java │ ├── to1_specialdao/ │ │ └── ToOneTarget2Dao.java │ └── to1_specialentity/ │ └── ToOneTarget2.java ├── DaoTestEntityAnnotation/ │ ├── build.gradle │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── org/ │ │ └── greenrobot/ │ │ └── greendao/ │ │ └── test/ │ │ └── entityannotation/ │ │ ├── CustomerOrderTest.java │ │ ├── CustomerTest.java │ │ ├── NotNullThingTest.java │ │ ├── OrderTest.java │ │ └── TypesInInnerClassTest.java │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── org/ │ └── greenrobot/ │ └── greendao/ │ └── test/ │ └── entityannotation/ │ ├── Customer.java │ ├── NotNullThing.java │ ├── Order.java │ └── TypesInInnerClass.java ├── DaoTestGenerator/ │ ├── build.gradle │ └── src/ │ └── org/ │ └── greenrobot/ │ └── greendao/ │ └── generator/ │ └── gentest/ │ └── TestDaoGenerator.java └── DaoTestPerformance/ ├── build.gradle └── src/ ├── androidTest/ │ └── java/ │ └── org/ │ └── greenrobot/ │ └── greendao/ │ └── performance/ │ ├── Benchmark.java │ ├── IndexedStringPerformanceTest.java │ ├── LoockupPerformanceTest.java │ ├── PerformanceTest.java │ ├── PerformanceTestNotNull.java │ ├── PerformanceTestNotNullIdentityScope.java │ ├── ReflectionPerformanceTest.java │ ├── StringGenerator.java │ └── target/ │ ├── ArrayUtils.java │ ├── LongHashMapAmarena2DZechner.java │ ├── LongHashMapJDBM.java │ └── LongSparseArray.java └── main/ └── AndroidManifest.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .gradle *.iml .idea/ out/ build/ local.properties ================================================ FILE: .travis.yml ================================================ # Use the Travis Container-Based Infrastructure sudo: false language: android jdk: - oraclejdk8 before_install: # Install the rest of tools (e.g., avdmanager) - yes | sdkmanager tools # Install the system image - yes | sdkmanager "system-images;android-18;default;armeabi-v7a" # Create and start emulator for the script. Meant to race the install task. - echo no | avdmanager create avd --force -n test -k "system-images;android-18;default;armeabi-v7a" - $ANDROID_HOME/emulator/emulator -avd test -no-audio -no-window & install: ./gradlew clean assemble assembleAndroidTest --stacktrace before_script: - android-wait-for-emulator - adb shell input keyevent 82 script: ./gradlew check connectedCheck -x :tests:DaoTestPerformance:connectedCheck --stacktrace before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ cache: directories: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ - $HOME/.android/build-cache ================================================ FILE: CONTRIBUTING.md ================================================ Before you create an Issue... ============================= There are better Places for Support ----------------------------------- We want your question to be answered, so it is important that you ask at the right place. Be aware that an issue tracker is not the best place to ask for support. An issue tracker is used to track issues (bugs or feature requests). Instead, please use [stackoverflow.com](https://stackoverflow.com/questions/tagged/greendao?sort=frequent) and use the tag [greendao](https://stackoverflow.com/tags/greendao/info) for your question. Examples for support questions that are more likely to be answered on StackOverflow: * Asking how something works * Asking how to use greenDAO in a specific scenario * Your app crashes/misbehaves and you are not sure why The perfect Issue Report ------------------------ A couple of simple steps can save time for everyone. Check before reporting: * It's not a support inquiry * You have read the docs * You searched the web and stackoverflow * You searched existing issues to avoid duplicates Reporting bugs: * Please investigate if is the bug is really caused by the library. Isolate the issue: what's the minimal code to reproduce the bug? * Bonus steps to gain extra karma points: once you isolated and identified the issue, you can prepare an push request. Submit an unit test causing the bug, and ideally a fix for the bug. Requesting features: * Ask yourself: is the feature useful for a majority users? One of our major goals is to keep the API simple and concise. We do not want to cover all possible use cases, but those that make 80% of users happy. Thanks for reading! =================== It's your feedback that makes maintaining this library fun. ================================================ FILE: DaoCore/.gitignore ================================================ /gradle.properties ================================================ FILE: DaoCore/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: DaoCore/NOTICE ================================================ greenrobot greenDAO (c) Copyright 2011-2016 by Markus Junginger / greenrobot.org All rights reserved This product includes software developed at greenrobot.org ================================================ FILE: DaoCore/build.gradle ================================================ apply plugin: 'java' group = 'org.greenrobot' archivesBaseName = 'greendao' version = rootProject.version sourceCompatibility = 1.7 targetCompatibility = 1.7 repositories { mavenCentral() } dependencies { compile project(':greendao-api') compileOnly 'com.google.android:android:4.1.1.4' compileOnly 'com.google.android:android-test:4.1.1.4' compileOnly 'com.google.android:annotations:4.1.1.4' compileOnly 'com.google.android:support-v4:r7' compileOnly 'io.reactivex:rxjava:1.1.8' compileOnly files('libs/sqlcipher.jar') } apply from: rootProject.file("gradle/publish.gradle") javadoc { failOnError = false title = " greenDAO ${version} API" options.bottom = 'Available under the Apache License, Version 2.0 - Copyright © 2011-2020 greenrobot.org. All Rights Reserved.' excludes = ['org/greenrobot/dao/internal', 'org/greenrobot/dao/Internal*'] def srcApi = project(':greendao-api').file('src/main/java/') if (!srcApi.directory) throw new GradleScriptException("Not a directory: ${srcApi}", null) source += srcApi doLast { copy { from '../javadoc-style' into "build/docs/javadoc/" } } } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from 'build/docs/javadoc' } task sourcesJar(type: Jar) { from sourceSets.main.allSource classifier = 'sources' } artifacts { // jar added by Java plugin. archives javadocJar archives sourcesJar } uploadArchives { repositories { mavenDeployer { // Basic definitions are defined in root project pom.project { name 'greenDAO' description 'greenDAO is a light and fast ORM for Android' licenses { license { name 'The Apache Software License, Version 2.0' url 'http://www.apache.org/licenses/LICENSE-2.0.txt' distribution 'repo' } } } } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/AbstractDao.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import android.database.CrossProcessCursor; import android.database.Cursor; import android.database.CursorWindow; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteStatement; import org.greenrobot.greendao.annotation.apihint.Experimental; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.database.DatabaseStatement; import org.greenrobot.greendao.identityscope.IdentityScope; import org.greenrobot.greendao.identityscope.IdentityScopeLong; import org.greenrobot.greendao.internal.DaoConfig; import org.greenrobot.greendao.internal.FastCursor; import org.greenrobot.greendao.internal.TableStatements; import org.greenrobot.greendao.query.Query; import org.greenrobot.greendao.query.QueryBuilder; import org.greenrobot.greendao.rx.RxDao; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import rx.schedulers.Schedulers; /** * Base class for all DAOs: Implements entity operations like insert, load, delete, and query. *

* This class is thread-safe. * * @param Entity type * @param Primary key (PK) type; use Void if entity does not have exactly one PK * @author Markus */ /* * When operating on TX, statements, or identity scope the following locking order must be met to avoid deadlocks: * * 1.) If not inside a TX already, begin a TX to acquire a DB connection (connection is to be handled like a lock) * * 2.) The DatabaseStatement * * 3.) identityScope */ public abstract class AbstractDao { protected final DaoConfig config; protected final Database db; protected final boolean isStandardSQLite; protected final IdentityScope identityScope; protected final IdentityScopeLong identityScopeLong; protected final TableStatements statements; protected final AbstractDaoSession session; protected final int pkOrdinal; private volatile RxDao rxDao; private volatile RxDao rxDaoPlain; public AbstractDao(DaoConfig config) { this(config, null); } @SuppressWarnings("unchecked") public AbstractDao(DaoConfig config, AbstractDaoSession daoSession) { this.config = config; this.session = daoSession; db = config.db; isStandardSQLite = db.getRawDatabase() instanceof SQLiteDatabase; identityScope = (IdentityScope) config.getIdentityScope(); if (identityScope instanceof IdentityScopeLong) { identityScopeLong = (IdentityScopeLong) identityScope; } else { identityScopeLong = null; } statements = config.statements; pkOrdinal = config.pkProperty != null ? config.pkProperty.ordinal : -1; } public AbstractDaoSession getSession() { return session; } TableStatements getStatements() { return config.statements; } public String getTablename() { return config.tablename; } public Property[] getProperties() { return config.properties; } public Property getPkProperty() { return config.pkProperty; } public String[] getAllColumns() { return config.allColumns; } public String[] getPkColumns() { return config.pkColumns; } public String[] getNonPkColumns() { return config.nonPkColumns; } /** * Loads the entity for the given PK. * * @param key a PK value or null * @return The entity or null, if no entity matched the PK value */ public T load(K key) { assertSinglePk(); if (key == null) { return null; } if (identityScope != null) { T entity = identityScope.get(key); if (entity != null) { return entity; } } String sql = statements.getSelectByKey(); String[] keyArray = new String[]{key.toString()}; Cursor cursor = db.rawQuery(sql, keyArray); return loadUniqueAndCloseCursor(cursor); } public T loadByRowId(long rowId) { String[] idArray = new String[]{Long.toString(rowId)}; Cursor cursor = db.rawQuery(statements.getSelectByRowId(), idArray); return loadUniqueAndCloseCursor(cursor); } protected T loadUniqueAndCloseCursor(Cursor cursor) { try { return loadUnique(cursor); } finally { cursor.close(); } } protected T loadUnique(Cursor cursor) { boolean available = cursor.moveToFirst(); if (!available) { return null; } else if (!cursor.isLast()) { throw new DaoException("Expected unique result, but count was " + cursor.getCount()); } return loadCurrent(cursor, 0, true); } /** Loads all available entities from the database. */ public List loadAll() { Cursor cursor = db.rawQuery(statements.getSelectAll(), null); return loadAllAndCloseCursor(cursor); } /** Detaches an entity from the identity scope (session). Subsequent query results won't return this object. */ public boolean detach(T entity) { if (identityScope != null) { K key = getKeyVerified(entity); return identityScope.detach(key, entity); } else { return false; } } /** * Detaches all entities (of type T) from the identity scope (session). Subsequent query results won't return any * previously loaded objects. */ public void detachAll() { if (identityScope != null) { identityScope.clear(); } } protected List loadAllAndCloseCursor(Cursor cursor) { try { return loadAllFromCursor(cursor); } finally { cursor.close(); } } /** * Inserts the given entities in the database using a transaction. * * @param entities The entities to insert. */ public void insertInTx(Iterable entities) { insertInTx(entities, isEntityUpdateable()); } /** * Inserts the given entities in the database using a transaction. * * @param entities The entities to insert. */ public void insertInTx(T... entities) { insertInTx(Arrays.asList(entities), isEntityUpdateable()); } /** * Inserts the given entities in the database using a transaction. The given entities will become tracked if the PK * is set. * * @param entities The entities to insert. * @param setPrimaryKey if true, the PKs of the given will be set after the insert; pass false to improve * performance. */ public void insertInTx(Iterable entities, boolean setPrimaryKey) { DatabaseStatement stmt = statements.getInsertStatement(); executeInsertInTx(stmt, entities, setPrimaryKey); } /** * Inserts or replaces the given entities in the database using a transaction. The given entities will become * tracked if the PK is set. * * @param entities The entities to insert. * @param setPrimaryKey if true, the PKs of the given will be set after the insert; pass false to improve * performance. */ public void insertOrReplaceInTx(Iterable entities, boolean setPrimaryKey) { DatabaseStatement stmt = statements.getInsertOrReplaceStatement(); executeInsertInTx(stmt, entities, setPrimaryKey); } /** * Inserts or replaces the given entities in the database using a transaction. * * @param entities The entities to insert. */ public void insertOrReplaceInTx(Iterable entities) { insertOrReplaceInTx(entities, isEntityUpdateable()); } /** * Inserts or replaces the given entities in the database using a transaction. * * @param entities The entities to insert. */ public void insertOrReplaceInTx(T... entities) { insertOrReplaceInTx(Arrays.asList(entities), isEntityUpdateable()); } private void executeInsertInTx(DatabaseStatement stmt, Iterable entities, boolean setPrimaryKey) { db.beginTransaction(); try { synchronized (stmt) { if (identityScope != null) { identityScope.lock(); } try { if (isStandardSQLite) { SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement(); for (T entity : entities) { bindValues(rawStmt, entity); if (setPrimaryKey) { long rowId = rawStmt.executeInsert(); updateKeyAfterInsertAndAttach(entity, rowId, false); } else { rawStmt.execute(); } } } else { for (T entity : entities) { bindValues(stmt, entity); if (setPrimaryKey) { long rowId = stmt.executeInsert(); updateKeyAfterInsertAndAttach(entity, rowId, false); } else { stmt.execute(); } } } } finally { if (identityScope != null) { identityScope.unlock(); } } } db.setTransactionSuccessful(); } finally { db.endTransaction(); } } /** * Insert an entity into the table associated with a concrete DAO. * * @return row ID of newly inserted entity */ public long insert(T entity) { return executeInsert(entity, statements.getInsertStatement(), true); } /** * Insert an entity into the table associated with a concrete DAO without setting key property. *

* Warning: This may be faster, but the entity should not be used anymore. The entity also won't be attached to * identity scope. * * @return row ID of newly inserted entity */ public long insertWithoutSettingPk(T entity) { return executeInsert(entity, statements.getInsertOrReplaceStatement(), false); } /** * Insert an entity into the table associated with a concrete DAO. * * @return row ID of newly inserted entity */ public long insertOrReplace(T entity) { return executeInsert(entity, statements.getInsertOrReplaceStatement(), true); } private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) { long rowId; if (db.isDbLockedByCurrentThread()) { rowId = insertInsideTx(entity, stmt); } else { // Do TX to acquire a connection before locking the stmt to avoid deadlocks db.beginTransaction(); try { rowId = insertInsideTx(entity, stmt); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } if (setKeyAndAttach) { updateKeyAfterInsertAndAttach(entity, rowId, true); } return rowId; } private long insertInsideTx(T entity, DatabaseStatement stmt) { synchronized (stmt) { if (isStandardSQLite) { SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement(); bindValues(rawStmt, entity); return rawStmt.executeInsert(); } else { bindValues(stmt, entity); return stmt.executeInsert(); } } } protected void updateKeyAfterInsertAndAttach(T entity, long rowId, boolean lock) { if (rowId != -1) { K key = updateKeyAfterInsert(entity, rowId); attachEntity(key, entity, lock); } else { // TODO When does this actually happen? Should we throw instead? DaoLog.w("Could not insert row (executeInsert returned -1)"); } } /** * "Saves" an entity to the database: depending on the existence of the key property, it will be inserted * (key is null) or updated (key is not null). *

* This is similar to {@link #insertOrReplace(Object)}, but may be more efficient, because if a key is present, * it does not have to query if that key already exists. */ public void save(T entity) { if (hasKey(entity)) { update(entity); } else { insert(entity); } } /** * Saves (see {@link #save(Object)}) the given entities in the database using a transaction. * * @param entities The entities to save. */ public void saveInTx(T... entities) { saveInTx(Arrays.asList(entities)); } /** * Saves (see {@link #save(Object)}) the given entities in the database using a transaction. * * @param entities The entities to save. */ public void saveInTx(Iterable entities) { int updateCount = 0; int insertCount = 0; for (T entity : entities) { if (hasKey(entity)) { updateCount++; } else { insertCount++; } } if (updateCount > 0 && insertCount > 0) { List toUpdate = new ArrayList<>(updateCount); List toInsert = new ArrayList<>(insertCount); for (T entity : entities) { if (hasKey(entity)) { toUpdate.add(entity); } else { toInsert.add(entity); } } db.beginTransaction(); try { updateInTx(toUpdate); insertInTx(toInsert); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } else if (insertCount > 0) { insertInTx(entities); } else if (updateCount > 0) { updateInTx(entities); } } /** Reads all available rows from the given cursor and returns a list of entities. */ protected List loadAllFromCursor(Cursor cursor) { int count = cursor.getCount(); if (count == 0) { return new ArrayList(); } List list = new ArrayList(count); CursorWindow window = null; boolean useFastCursor = false; if (cursor instanceof CrossProcessCursor) { window = ((CrossProcessCursor) cursor).getWindow(); if (window != null) { // E.g. Robolectric has no Window at this point if (window.getNumRows() == count) { cursor = new FastCursor(window); useFastCursor = true; } else { DaoLog.d("Window vs. result size: " + window.getNumRows() + "/" + count); } } } if (cursor.moveToFirst()) { if (identityScope != null) { identityScope.lock(); identityScope.reserveRoom(count); } try { if (!useFastCursor && window != null && identityScope != null) { loadAllUnlockOnWindowBounds(cursor, window, list); } else { do { list.add(loadCurrent(cursor, 0, false)); } while (cursor.moveToNext()); } } finally { if (identityScope != null) { identityScope.unlock(); } } } return list; } private void loadAllUnlockOnWindowBounds(Cursor cursor, CursorWindow window, List list) { int windowEnd = window.getStartPosition() + window.getNumRows(); for (int row = 0; ; row++) { list.add(loadCurrent(cursor, 0, false)); row++; if (row >= windowEnd) { window = moveToNextUnlocked(cursor); if (window == null) { break; } windowEnd = window.getStartPosition() + window.getNumRows(); } else { if (!cursor.moveToNext()) { break; } } } } /** * Unlock identityScope during cursor.moveToNext() when it is about to fill the window (needs a db connection): * We should not hold the lock while trying to acquire a db connection to avoid deadlocks. */ private CursorWindow moveToNextUnlocked(Cursor cursor) { identityScope.unlock(); try { if (cursor.moveToNext()) { return ((CrossProcessCursor) cursor).getWindow(); } else { return null; } } finally { identityScope.lock(); } } /** Internal use only. Considers identity scope. */ final protected T loadCurrent(Cursor cursor, int offset, boolean lock) { if (identityScopeLong != null) { if (offset != 0) { // Occurs with deep loads (left outer joins) if (cursor.isNull(pkOrdinal + offset)) { return null; } } long key = cursor.getLong(pkOrdinal + offset); T entity = lock ? identityScopeLong.get2(key) : identityScopeLong.get2NoLock(key); if (entity != null) { return entity; } else { entity = readEntity(cursor, offset); attachEntity(entity); if (lock) { identityScopeLong.put2(key, entity); } else { identityScopeLong.put2NoLock(key, entity); } return entity; } } else if (identityScope != null) { K key = readKey(cursor, offset); if (offset != 0 && key == null) { // Occurs with deep loads (left outer joins) return null; } T entity = lock ? identityScope.get(key) : identityScope.getNoLock(key); if (entity != null) { return entity; } else { entity = readEntity(cursor, offset); attachEntity(key, entity, lock); return entity; } } else { // Check offset, assume a value !=0 indicating a potential outer join, so check PK if (offset != 0) { K key = readKey(cursor, offset); if (key == null) { // Occurs with deep loads (left outer joins) return null; } } T entity = readEntity(cursor, offset); attachEntity(entity); return entity; } } /** Internal use only. Considers identity scope. */ final protected O loadCurrentOther(AbstractDao dao, Cursor cursor, int offset) { return dao.loadCurrent(cursor, offset, /* TODO check this */true); } /** A raw-style query where you can pass any WHERE clause and arguments. */ public List queryRaw(String where, String... selectionArg) { Cursor cursor = db.rawQuery(statements.getSelectAll() + where, selectionArg); return loadAllAndCloseCursor(cursor); } /** * Creates a repeatable {@link Query} object based on the given raw SQL where you can pass any WHERE clause and * arguments. */ public Query queryRawCreate(String where, Object... selectionArg) { List argList = Arrays.asList(selectionArg); return queryRawCreateListArgs(where, argList); } /** * Creates a repeatable {@link Query} object based on the given raw SQL where you can pass any WHERE clause and * arguments. */ public Query queryRawCreateListArgs(String where, Collection selectionArg) { return Query.internalCreate(this, statements.getSelectAll() + where, selectionArg.toArray()); } public void deleteAll() { // String sql = SqlUtils.createSqlDelete(config.tablename, null); // db.execSQL(sql); db.execSQL("DELETE FROM '" + config.tablename + "'"); if (identityScope != null) { identityScope.clear(); } } /** Deletes the given entity from the database. Currently, only single value PK entities are supported. */ public void delete(T entity) { assertSinglePk(); K key = getKeyVerified(entity); deleteByKey(key); } /** Deletes an entity with the given PK from the database. Currently, only single value PK entities are supported. */ public void deleteByKey(K key) { assertSinglePk(); DatabaseStatement stmt = statements.getDeleteStatement(); if (db.isDbLockedByCurrentThread()) { synchronized (stmt) { deleteByKeyInsideSynchronized(key, stmt); } } else { // Do TX to acquire a connection before locking the stmt to avoid deadlocks db.beginTransaction(); try { synchronized (stmt) { deleteByKeyInsideSynchronized(key, stmt); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } } if (identityScope != null) { identityScope.remove(key); } } private void deleteByKeyInsideSynchronized(K key, DatabaseStatement stmt) { if (key instanceof Long) { stmt.bindLong(1, (Long) key); } else if (key == null) { throw new DaoException("Cannot delete entity, key is null"); } else { stmt.bindString(1, key.toString()); } stmt.execute(); } private void deleteInTxInternal(Iterable entities, Iterable keys) { assertSinglePk(); DatabaseStatement stmt = statements.getDeleteStatement(); List keysToRemoveFromIdentityScope = null; db.beginTransaction(); try { synchronized (stmt) { if (identityScope != null) { identityScope.lock(); keysToRemoveFromIdentityScope = new ArrayList(); } try { if (entities != null) { for (T entity : entities) { K key = getKeyVerified(entity); deleteByKeyInsideSynchronized(key, stmt); if (keysToRemoveFromIdentityScope != null) { keysToRemoveFromIdentityScope.add(key); } } } if (keys != null) { for (K key : keys) { deleteByKeyInsideSynchronized(key, stmt); if (keysToRemoveFromIdentityScope != null) { keysToRemoveFromIdentityScope.add(key); } } } } finally { if (identityScope != null) { identityScope.unlock(); } } } db.setTransactionSuccessful(); if (keysToRemoveFromIdentityScope != null && identityScope != null) { identityScope.remove(keysToRemoveFromIdentityScope); } } finally { db.endTransaction(); } } /** * Deletes the given entities in the database using a transaction. * * @param entities The entities to delete. */ public void deleteInTx(Iterable entities) { deleteInTxInternal(entities, null); } /** * Deletes the given entities in the database using a transaction. * * @param entities The entities to delete. */ public void deleteInTx(T... entities) { deleteInTxInternal(Arrays.asList(entities), null); } /** * Deletes all entities with the given keys in the database using a transaction. * * @param keys Keys of the entities to delete. */ public void deleteByKeyInTx(Iterable keys) { deleteInTxInternal(null, keys); } /** * Deletes all entities with the given keys in the database using a transaction. * * @param keys Keys of the entities to delete. */ public void deleteByKeyInTx(K... keys) { deleteInTxInternal(null, Arrays.asList(keys)); } /** Resets all locally changed properties of the entity by reloading the values from the database. */ public void refresh(T entity) { assertSinglePk(); K key = getKeyVerified(entity); String sql = statements.getSelectByKey(); String[] keyArray = new String[]{key.toString()}; Cursor cursor = db.rawQuery(sql, keyArray); try { boolean available = cursor.moveToFirst(); if (!available) { throw new DaoException("Entity does not exist in the database anymore: " + entity.getClass() + " with key " + key); } else if (!cursor.isLast()) { throw new DaoException("Expected unique result, but count was " + cursor.getCount()); } readEntity(cursor, entity, 0); attachEntity(key, entity, true); } finally { cursor.close(); } } public void update(T entity) { assertSinglePk(); DatabaseStatement stmt = statements.getUpdateStatement(); if (db.isDbLockedByCurrentThread()) { synchronized (stmt) { if (isStandardSQLite) { updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true); } else { updateInsideSynchronized(entity, stmt, true); } } } else { // Do TX to acquire a connection before locking the stmt to avoid deadlocks db.beginTransaction(); try { synchronized (stmt) { updateInsideSynchronized(entity, stmt, true); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } } } public QueryBuilder queryBuilder() { return QueryBuilder.internalCreate(this); } protected void updateInsideSynchronized(T entity, DatabaseStatement stmt, boolean lock) { // To do? Check if it's worth not to bind PKs here (performance). bindValues(stmt, entity); int index = config.allColumns.length + 1; K key = getKey(entity); if (key instanceof Long) { stmt.bindLong(index, (Long) key); } else if (key == null) { throw new DaoException("Cannot update entity without key - was it inserted before?"); } else { stmt.bindString(index, key.toString()); } stmt.execute(); attachEntity(key, entity, lock); } protected void updateInsideSynchronized(T entity, SQLiteStatement stmt, boolean lock) { // To do? Check if it's worth not to bind PKs here (performance). bindValues(stmt, entity); int index = config.allColumns.length + 1; K key = getKey(entity); if (key instanceof Long) { stmt.bindLong(index, (Long) key); } else if (key == null) { throw new DaoException("Cannot update entity without key - was it inserted before?"); } else { stmt.bindString(index, key.toString()); } stmt.execute(); attachEntity(key, entity, lock); } /** * Attaches the entity to the identity scope. Calls attachEntity(T entity). * * @param key Needed only for identity scope, pass null if there's none. * @param entity The entitiy to attach */ protected final void attachEntity(K key, T entity, boolean lock) { attachEntity(entity); if (identityScope != null && key != null) { if (lock) { identityScope.put(key, entity); } else { identityScope.putNoLock(key, entity); } } } /** * Sub classes with relations additionally set the DaoMaster here. Must be called before the entity is attached to * the identity scope. * * @param entity The entitiy to attach */ protected void attachEntity(T entity) { } /** * Updates the given entities in the database using a transaction. * * @param entities The entities to insert. */ public void updateInTx(Iterable entities) { DatabaseStatement stmt = statements.getUpdateStatement(); db.beginTransaction(); // txEx: just to preserve original exception in case another exceptions is thrown in endTransaction() RuntimeException txEx = null; try { synchronized (stmt) { if (identityScope != null) { identityScope.lock(); } try { if (isStandardSQLite) { SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement(); for (T entity : entities) { updateInsideSynchronized(entity, rawStmt, false); } } else { for (T entity : entities) { updateInsideSynchronized(entity, stmt, false); } } } finally { if (identityScope != null) { identityScope.unlock(); } } } db.setTransactionSuccessful(); } catch (RuntimeException e) { txEx = e; } finally { try { db.endTransaction(); } catch (RuntimeException e) { if (txEx != null) { DaoLog.w("Could not end transaction (rethrowing initial exception)", e); throw txEx; } else { throw e; } } } if (txEx != null) { throw txEx; } } /** * Updates the given entities in the database using a transaction. * * @param entities The entities to update. */ public void updateInTx(T... entities) { updateInTx(Arrays.asList(entities)); } protected void assertSinglePk() { if (config.pkColumns.length != 1) { throw new DaoException(this + " (" + config.tablename + ") does not have a single-column primary key"); } } public long count() { return statements.getCountStatement().simpleQueryForLong(); } /** See {@link #getKey(Object)}, but guarantees that the returned key is never null (throws if null). */ protected K getKeyVerified(T entity) { K key = getKey(entity); if (key == null) { if (entity == null) { throw new NullPointerException("Entity may not be null"); } else { throw new DaoException("Entity has no key"); } } else { return key; } } /** * The returned RxDao is a special DAO that let's you interact with Rx Observables without any Scheduler set * for subscribeOn. * * @see #rx() */ @Experimental public RxDao rxPlain() { if (rxDaoPlain == null) { rxDaoPlain = new RxDao<>(this); } return rxDaoPlain; } /** * The returned RxDao is a special DAO that let's you interact with Rx Observables using RX's IO scheduler for * subscribeOn. * * @see #rxPlain() */ @Experimental public RxDao rx() { if (rxDao == null) { rxDao = new RxDao<>(this, Schedulers.io()); } return rxDao; } /** Gets the SQLiteDatabase for custom database access. Not needed for greenDAO entities. */ public Database getDatabase() { return db; } /** Reads the values from the current position of the given cursor and returns a new entity. */ abstract protected T readEntity(Cursor cursor, int offset); /** Reads the key from the current position of the given cursor, or returns null if there's no single-value key. */ abstract protected K readKey(Cursor cursor, int offset); /** Reads the values from the current position of the given cursor into an existing entity. */ abstract protected void readEntity(Cursor cursor, T entity, int offset); /** Binds the entity's values to the statement. Make sure to synchronize the statement outside of the method. */ abstract protected void bindValues(DatabaseStatement stmt, T entity); /** * Binds the entity's values to the statement. Make sure to synchronize the enclosing DatabaseStatement outside * of the method. */ protected abstract void bindValues(SQLiteStatement stmt, T entity); /** * Updates the entity's key if possible (only for Long PKs currently). This method must always return the entity's * key regardless of whether the key existed before or not. */ abstract protected K updateKeyAfterInsert(T entity, long rowId); /** * Returns the value of the primary key, if the entity has a single primary key, or, if not, null. Returns null if * entity is null. */ abstract protected K getKey(T entity); /** * Returns true if the entity is not null, and has a non-null key, which is also != 0. * entity is null. */ abstract protected boolean hasKey(T entity); /** Returns true if the Entity class can be updated, e.g. for setting the PK after insert. */ abstract protected boolean isEntityUpdateable(); } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/AbstractDaoMaster.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import java.util.HashMap; import java.util.Map; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.identityscope.IdentityScopeType; import org.greenrobot.greendao.internal.DaoConfig; /** * The master of dao will guide you: start dao sessions with the master. * * @author Markus */ public abstract class AbstractDaoMaster { protected final Database db; protected final int schemaVersion; protected final Map>, DaoConfig> daoConfigMap; public AbstractDaoMaster(Database db, int schemaVersion) { this.db = db; this.schemaVersion = schemaVersion; daoConfigMap = new HashMap>, DaoConfig>(); } protected void registerDaoClass(Class> daoClass) { DaoConfig daoConfig = new DaoConfig(db, daoClass); daoConfigMap.put(daoClass, daoConfig); } public int getSchemaVersion() { return schemaVersion; } /** Gets the SQLiteDatabase for custom database access. Not needed for greenDAO entities. */ public Database getDatabase() { return db; } public abstract AbstractDaoSession newSession(); public abstract AbstractDaoSession newSession(IdentityScopeType type); } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/AbstractDaoSession.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import org.greenrobot.greendao.annotation.apihint.Experimental; import org.greenrobot.greendao.async.AsyncSession; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.query.QueryBuilder; import org.greenrobot.greendao.rx.RxTransaction; import rx.schedulers.Schedulers; /** * DaoSession gives you access to your DAOs, offers convenient persistence methods, and also serves as a session cache.
*
* To access the DAOs, call the get{entity}Dao methods by the generated DaoSession sub class.
*
* DaoSession offers many of the available persistence operations on entities as a convenience. Consider using DAOs * directly to access all available operations, especially if you call a lot of operations on a single entity type to * avoid the overhead imposed by DaoSession (the overhead is small, but it may add up).
*
* By default, the DaoSession has a session cache (IdentityScopeType.Session). The session cache is not just a plain * data cache to improve performance, but also manages object identities. For example, if you load the same entity twice * in a query, you will get a single Java object instead of two when using a session cache. This is particular useful * for relations pointing to a common set of entities. * * This class is thread-safe. * * @author Markus * */ public class AbstractDaoSession { private final Database db; private final Map, AbstractDao> entityToDao; private volatile RxTransaction rxTxPlain; private volatile RxTransaction rxTxIo; public AbstractDaoSession(Database db) { this.db = db; this.entityToDao = new HashMap, AbstractDao>(); } protected void registerDao(Class entityClass, AbstractDao dao) { entityToDao.put(entityClass, dao); } /** Convenient call for {@link AbstractDao#insert(Object)}. */ public long insert(T entity) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entity.getClass()); return dao.insert(entity); } /** Convenient call for {@link AbstractDao#insertOrReplace(Object)}. */ public long insertOrReplace(T entity) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entity.getClass()); return dao.insertOrReplace(entity); } /** Convenient call for {@link AbstractDao#refresh(Object)}. */ public void refresh(T entity) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entity.getClass()); dao.refresh(entity); } /** Convenient call for {@link AbstractDao#update(Object)}. */ public void update(T entity) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entity.getClass()); dao.update(entity); } /** Convenient call for {@link AbstractDao#delete(Object)}. */ public void delete(T entity) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entity.getClass()); dao.delete(entity); } /** Convenient call for {@link AbstractDao#deleteAll()}. */ public void deleteAll(Class entityClass) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entityClass); dao.deleteAll(); } /** Convenient call for {@link AbstractDao#load(Object)}. */ public T load(Class entityClass, K key) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entityClass); return dao.load(key); } /** Convenient call for {@link AbstractDao#loadAll()}. */ public List loadAll(Class entityClass) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entityClass); return dao.loadAll(); } /** Convenient call for {@link AbstractDao#queryRaw(String, String...)}. */ public List queryRaw(Class entityClass, String where, String... selectionArgs) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entityClass); return dao.queryRaw(where, selectionArgs); } /** Convenient call for {@link AbstractDao#queryBuilder()}. */ public QueryBuilder queryBuilder(Class entityClass) { @SuppressWarnings("unchecked") AbstractDao dao = (AbstractDao) getDao(entityClass); return dao.queryBuilder(); } public AbstractDao getDao(Class entityClass) { AbstractDao dao = entityToDao.get(entityClass); if (dao == null) { throw new DaoException("No DAO registered for " + entityClass); } return dao; } /** * Run the given Runnable inside a database transaction. If you except a result, consider callInTx. */ public void runInTx(Runnable runnable) { db.beginTransaction(); try { runnable.run(); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } /** * Calls the given Callable inside a database transaction and returns the result of the Callable. If you don't * except a result, consider runInTx. */ public V callInTx(Callable callable) throws Exception { db.beginTransaction(); try { V result = callable.call(); db.setTransactionSuccessful(); return result; } finally { db.endTransaction(); } } /** * Like {@link #callInTx(Callable)} but does not require Exception handling (rethrows an Exception as a runtime * DaoException). */ public V callInTxNoException(Callable callable) { db.beginTransaction(); try { V result; try { result = callable.call(); } catch (Exception e) { throw new DaoException("Callable failed", e); } db.setTransactionSuccessful(); return result; } finally { db.endTransaction(); } } /** Gets the Database for custom database access. Not needed for greenDAO entities. */ public Database getDatabase() { return db; } /** Allows to inspect the meta model using DAOs (e.g. querying table names or properties). */ public Collection> getAllDaos() { return Collections.unmodifiableCollection(entityToDao.values()); } /** * Creates a new {@link AsyncSession} to issue asynchronous entity operations. See {@link AsyncSession} for details. */ public AsyncSession startAsyncSession() { return new AsyncSession(this); } /** * The returned {@link RxTransaction} allows DB transactions using Rx Observables without any Scheduler set for * subscribeOn. * * @see #rxTx() */ @Experimental public RxTransaction rxTxPlain() { if (rxTxPlain == null) { rxTxPlain = new RxTransaction(this); } return rxTxPlain; } /** * The returned {@link RxTransaction} allows DB transactions using Rx Observables using RX's IO scheduler for * subscribeOn. * * @see #rxTxPlain() */ @Experimental public RxTransaction rxTx() { if (rxTxIo == null) { rxTxIo = new RxTransaction(this, Schedulers.io()); } return rxTxIo; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/DaoException.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import android.database.SQLException; /** * Exception thrown when something goes wrong in the DAO/ORM layer. * * @author Markus * */ public class DaoException extends SQLException { private static final long serialVersionUID = -5877937327907457779L; public DaoException() { } public DaoException(String error) { super(error); } public DaoException(String error, Throwable cause) { super(error); safeInitCause(cause); } public DaoException(Throwable th) { safeInitCause(th); } protected void safeInitCause(Throwable cause) { try { initCause(cause); } catch (Throwable e) { DaoLog.e("Could not set initial cause", e); DaoLog.e( "Initial cause is:", cause); } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/DaoLog.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import android.util.Log; /** * Internal greenDAO logger class. A wrapper around the Android Log class providing a static Log Tag. * * @author markus * */ public class DaoLog { private final static String TAG = "greenDAO"; public static final int VERBOSE = 2; public static final int DEBUG = 3; public static final int INFO = 4; public static final int WARN = 5; public static final int ERROR = 6; public static final int ASSERT = 7; public static boolean isLoggable(int level) { return Log.isLoggable(TAG, level); } public static String getStackTraceString(Throwable th) { return Log.getStackTraceString(th); } public static int println(int level, String msg) { return Log.println(level, TAG, msg); } public static int v(String msg) { return Log.v(TAG, msg); } public static int v(String msg, Throwable th) { return Log.v(TAG, msg, th); } public static int d(String msg) { return Log.d(TAG, msg); } public static int d(String msg, Throwable th) { return Log.d(TAG, msg, th); } public static int i(String msg) { return Log.i(TAG, msg); } public static int i(String msg, Throwable th) { return Log.i(TAG, msg, th); } public static int w(String msg) { return Log.w(TAG, msg); } public static int w(String msg, Throwable th) { return Log.w(TAG, msg, th); } public static int w(Throwable th) { return Log.w(TAG, th); } public static int e(String msg) { return Log.w(TAG, msg); } public static int e(String msg, Throwable th) { return Log.e(TAG, msg, th); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/DbUtils.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import android.content.Context; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import org.greenrobot.greendao.database.Database; /** Database utils, for example to execute SQL scripts */ // TODO add unit tests public class DbUtils { public static void vacuum(Database db) { db.execSQL("VACUUM"); } /** * Calls {@link #executeSqlScript(Context, Database, String, boolean)} with transactional set to true. * * @return number of statements executed. */ public static int executeSqlScript(Context context, Database db, String assetFilename) throws IOException { return executeSqlScript(context, db, assetFilename, true); } /** * Executes the given SQL asset in the given database (SQL file should be UTF-8). The database file may contain * multiple SQL statements. Statements are split using a simple regular expression (something like * "semicolon before a line break"), not by analyzing the SQL syntax. This will work for many SQL files, but check * yours. * * @return number of statements executed. */ public static int executeSqlScript(Context context, Database db, String assetFilename, boolean transactional) throws IOException { byte[] bytes = readAsset(context, assetFilename); String sql = new String(bytes, "UTF-8"); String[] lines = sql.split(";(\\s)*[\n\r]"); int count; if (transactional) { count = executeSqlStatementsInTx(db, lines); } else { count = executeSqlStatements(db, lines); } DaoLog.i("Executed " + count + " statements from SQL script '" + assetFilename + "'"); return count; } public static int executeSqlStatementsInTx(Database db, String[] statements) { db.beginTransaction(); try { int count = executeSqlStatements(db, statements); db.setTransactionSuccessful(); return count; } finally { db.endTransaction(); } } public static int executeSqlStatements(Database db, String[] statements) { int count = 0; for (String line : statements) { line = line.trim(); if (line.length() > 0) { db.execSQL(line); count++; } } return count; } /** * Copies all available data from in to out without closing any stream. * * @return number of bytes copied */ public static int copyAllBytes(InputStream in, OutputStream out) throws IOException { int byteCount = 0; byte[] buffer = new byte[4096]; while (true) { int read = in.read(buffer); if (read == -1) { break; } out.write(buffer, 0, read); byteCount += read; } return byteCount; } public static byte[] readAllBytes(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); copyAllBytes(in, out); return out.toByteArray(); } public static byte[] readAsset(Context context, String filename) throws IOException { InputStream in = context.getResources().getAssets().open(filename); try { return readAllBytes(in); } finally { in.close(); } } public static void logTableDump(SQLiteDatabase db, String tablename) { Cursor cursor = db.query(tablename, null, null, null, null, null, null); try { String dump = DatabaseUtils.dumpCursorToString(cursor); DaoLog.d(dump); } finally { cursor.close(); } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/InternalQueryDaoAccess.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import java.util.List; import android.database.Cursor; import org.greenrobot.greendao.internal.TableStatements; /** For internal use by greenDAO only. */ public final class InternalQueryDaoAccess { private final AbstractDao dao; public InternalQueryDaoAccess(AbstractDao abstractDao) { dao = abstractDao; } public T loadCurrent(Cursor cursor, int offset, boolean lock) { return dao.loadCurrent(cursor, offset, lock); } public List loadAllAndCloseCursor(Cursor cursor) { return dao.loadAllAndCloseCursor(cursor); } public T loadUniqueAndCloseCursor(Cursor cursor) { return dao.loadUniqueAndCloseCursor(cursor); } public TableStatements getStatements() { return dao.getStatements(); } public static TableStatements getStatements(AbstractDao dao) { return dao.getStatements(); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/InternalUnitTestDaoAccess.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import android.database.Cursor; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.identityscope.IdentityScope; import org.greenrobot.greendao.internal.DaoConfig; import java.lang.reflect.Constructor; /** Reserved for internal unit tests that want to access some non-public methods. Don't use for anything else. */ public class InternalUnitTestDaoAccess { private final AbstractDao dao; public InternalUnitTestDaoAccess(Database db, Class> daoClass, IdentityScope identityScope) throws Exception { DaoConfig daoConfig = new DaoConfig(db, daoClass); daoConfig.setIdentityScope(identityScope); Constructor> constructor = daoClass.getConstructor(DaoConfig.class); dao = constructor.newInstance(daoConfig); } public K getKey(T entity) { return dao.getKey(entity); } public Property[] getProperties() { return dao.getProperties(); } public boolean isEntityUpdateable() { return dao.isEntityUpdateable(); } public T readEntity(Cursor cursor, int offset) { return dao.readEntity(cursor, offset); } public K readKey(Cursor cursor, int offset) { return dao.readKey(cursor, offset); } public AbstractDao getDao() { return dao; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/Property.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao; import java.util.Collection; import org.greenrobot.greendao.internal.SqlUtils; import org.greenrobot.greendao.query.WhereCondition; import org.greenrobot.greendao.query.WhereCondition.PropertyCondition; /** * Meta data describing a property mapped to a database column; used to create WhereCondition object used by the query builder. * * @author Markus */ public class Property { public final int ordinal; public final Class type; public final String name; public final boolean primaryKey; public final String columnName; public Property(int ordinal, Class type, String name, boolean primaryKey, String columnName) { this.ordinal = ordinal; this.type = type; this.name = name; this.primaryKey = primaryKey; this.columnName = columnName; } /** Creates an "equal ('=')" condition for this property. */ public WhereCondition eq(Object value) { return new PropertyCondition(this, "=?", value); } /** Creates an "not equal ('<>')" condition for this property. */ public WhereCondition notEq(Object value) { return new PropertyCondition(this, "<>?", value); } /** Creates an "LIKE" condition for this property. */ public WhereCondition like(String value) { return new PropertyCondition(this, " LIKE ?", value); } /** Creates an "BETWEEN ... AND ..." condition for this property. */ public WhereCondition between(Object value1, Object value2) { Object[] values = { value1, value2 }; return new PropertyCondition(this, " BETWEEN ? AND ?", values); } /** Creates an "IN (..., ..., ...)" condition for this property. */ public WhereCondition in(Object... inValues) { StringBuilder condition = new StringBuilder(" IN ("); SqlUtils.appendPlaceholders(condition, inValues.length).append(')'); return new PropertyCondition(this, condition.toString(), inValues); } /** Creates an "IN (..., ..., ...)" condition for this property. */ public WhereCondition in(Collection inValues) { return in(inValues.toArray()); } /** Creates an "NOT IN (..., ..., ...)" condition for this property. */ public WhereCondition notIn(Object... notInValues) { StringBuilder condition = new StringBuilder(" NOT IN ("); SqlUtils.appendPlaceholders(condition, notInValues.length).append(')'); return new PropertyCondition(this, condition.toString(), notInValues); } /** Creates an "NOT IN (..., ..., ...)" condition for this property. */ public WhereCondition notIn(Collection notInValues) { return notIn(notInValues.toArray()); } /** Creates an "greater than ('>')" condition for this property. */ public WhereCondition gt(Object value) { return new PropertyCondition(this, ">?", value); } /** Creates an "less than ('<')" condition for this property. */ public WhereCondition lt(Object value) { return new PropertyCondition(this, "=?", value); } /** Creates an "less or equal ('<=')" condition for this property. */ public WhereCondition le(Object value) { return new PropertyCondition(this, "<=?", value); } /** Creates an "IS NULL" condition for this property. */ public WhereCondition isNull() { return new PropertyCondition(this, " IS NULL"); } /** Creates an "IS NOT NULL" condition for this property. */ public WhereCondition isNotNull() { return new PropertyCondition(this, " IS NOT NULL"); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/async/AsyncDaoException.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.async; import org.greenrobot.greendao.DaoException; /** * Used here: {@link AsyncOperation#getResult()}. * * @author Markus */ public class AsyncDaoException extends DaoException { private static final long serialVersionUID = 5872157552005102382L; private final AsyncOperation failedOperation; public AsyncDaoException(AsyncOperation failedOperation, Throwable cause) { super(cause); this.failedOperation = failedOperation; } public AsyncOperation getFailedOperation() { return failedOperation; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/async/AsyncOperation.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.async; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.database.Database; /** * An operation that will be enqueued for asynchronous execution. * * @author Markus * @see AsyncSession */ // TODO Implement Future public class AsyncOperation { public enum OperationType { Insert, InsertInTxIterable, InsertInTxArray, // InsertOrReplace, InsertOrReplaceInTxIterable, InsertOrReplaceInTxArray, // Update, UpdateInTxIterable, UpdateInTxArray, // Delete, DeleteInTxIterable, DeleteInTxArray, // DeleteByKey, DeleteAll, // TransactionRunnable, TransactionCallable, // QueryList, QueryUnique, // Load, LoadAll, // Count, Refresh } public static final int FLAG_MERGE_TX = 1; /** TODO unused, just an idea */ public static final int FLAG_STOP_QUEUE_ON_EXCEPTION = 1 << 1; public static final int FLAG_TRACK_CREATOR_STACKTRACE = 1 << 2; final OperationType type; final AbstractDao dao; private final Database database; /** Entity, Iterable, Entity[], or Runnable. */ final Object parameter; final int flags; volatile long timeStarted; volatile long timeCompleted; private volatile boolean completed; volatile Throwable throwable; final Exception creatorStacktrace; volatile Object result; volatile int mergedOperationsCount; int sequenceNumber; @SuppressWarnings("unchecked") /** Either supply dao or database (set other to null). */ AsyncOperation(OperationType type, AbstractDao dao, Database database, Object parameter, int flags) { this.type = type; this.flags = flags; this.dao = (AbstractDao) dao; this.database = database; this.parameter = parameter; creatorStacktrace = (flags & FLAG_TRACK_CREATOR_STACKTRACE) != 0 ? new Exception("AsyncOperation was created here") : null; } public Throwable getThrowable() { return throwable; } public void setThrowable(Throwable throwable) { this.throwable = throwable; } public OperationType getType() { return type; } public Object getParameter() { return parameter; } /** * The operation's result after it has completed. Waits until a result is available. * * @return The operation's result or null if the operation type does not produce any result. * @throws {@link AsyncDaoException} if the operation produced an exception * @see #waitForCompletion() */ public synchronized Object getResult() { if (!completed) { waitForCompletion(); } if (throwable != null) { throw new AsyncDaoException(this, throwable); } return result; } /** @return true if this operation may be merged with others into a single database transaction. */ public boolean isMergeTx() { return (flags & FLAG_MERGE_TX) != 0; } Database getDatabase() { return database != null ? database : dao.getDatabase(); } /** * @return true if this operation is mergeable with the given operation. Checks for null, {@link #FLAG_MERGE_TX}, * and if the database instances match. */ boolean isMergeableWith(AsyncOperation other) { return other != null && isMergeTx() && other.isMergeTx() && getDatabase() == other.getDatabase(); } public long getTimeStarted() { return timeStarted; } public long getTimeCompleted() { return timeCompleted; } public long getDuration() { if (timeCompleted == 0) { throw new DaoException("This operation did not yet complete"); } else { return timeCompleted - timeStarted; } } public boolean isFailed() { return throwable != null; } public boolean isCompleted() { return completed; } /** * Waits until the operation is complete. If the thread gets interrupted, any {@link InterruptedException} will be * rethrown as a {@link DaoException}. * * @return Result if any, see {@link #getResult()} */ public synchronized Object waitForCompletion() { while (!completed) { try { wait(); } catch (InterruptedException e) { throw new DaoException("Interrupted while waiting for operation to complete", e); } } return result; } /** * Waits until the operation is complete, but at most the given amount of milliseconds.If the thread gets * interrupted, any {@link InterruptedException} will be rethrown as a {@link DaoException}. * * @return true if the operation completed in the given time frame. */ public synchronized boolean waitForCompletion(int maxMillis) { if (!completed) { try { wait(maxMillis); } catch (InterruptedException e) { throw new DaoException("Interrupted while waiting for operation to complete", e); } } return completed; } /** Called when the operation is done. Notifies any threads waiting for this operation's completion. */ synchronized void setCompleted() { completed = true; notifyAll(); } public boolean isCompletedSucessfully() { return completed && throwable == null; } /** * If this operation was successfully merged with other operation into a single TX, this will give the count of * merged operations. If the operation was not merged, it will be 0. */ public int getMergedOperationsCount() { return mergedOperationsCount; } /** * Each operation get a unique sequence number when the operation is enqueued. Can be used for efficiently * identifying/mapping operations. */ public int getSequenceNumber() { return sequenceNumber; } /** Reset to prepare another execution run. */ void reset() { timeStarted = 0; timeCompleted = 0; completed = false; throwable = null; result = null; mergedOperationsCount = 0; } /** * The stacktrace is captured using an exception if {@link #FLAG_TRACK_CREATOR_STACKTRACE} was used (null * otherwise). */ public Exception getCreatorStacktrace() { return creatorStacktrace; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/async/AsyncOperationExecutor.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.async; import android.os.Handler; import android.os.Looper; import android.os.Message; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.DaoLog; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.query.Query; import java.util.ArrayList; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; class AsyncOperationExecutor implements Runnable, Handler.Callback { private static ExecutorService executorService = Executors.newCachedThreadPool(); private final BlockingQueue queue; private volatile boolean executorRunning; private volatile int maxOperationCountToMerge; private volatile AsyncOperationListener listener; private volatile AsyncOperationListener listenerMainThread; private volatile int waitForMergeMillis; private int countOperationsEnqueued; private int countOperationsCompleted; private Handler handlerMainThread; private int lastSequenceNumber; AsyncOperationExecutor() { queue = new LinkedBlockingQueue(); maxOperationCountToMerge = 50; waitForMergeMillis = 50; } public void enqueue(AsyncOperation operation) { synchronized (this) { operation.sequenceNumber = ++lastSequenceNumber; queue.add(operation); countOperationsEnqueued++; if (!executorRunning) { executorRunning = true; executorService.execute(this); } } } public int getMaxOperationCountToMerge() { return maxOperationCountToMerge; } public void setMaxOperationCountToMerge(int maxOperationCountToMerge) { this.maxOperationCountToMerge = maxOperationCountToMerge; } public int getWaitForMergeMillis() { return waitForMergeMillis; } public void setWaitForMergeMillis(int waitForMergeMillis) { this.waitForMergeMillis = waitForMergeMillis; } public AsyncOperationListener getListener() { return listener; } public void setListener(AsyncOperationListener listener) { this.listener = listener; } public AsyncOperationListener getListenerMainThread() { return listenerMainThread; } public void setListenerMainThread(AsyncOperationListener listenerMainThread) { this.listenerMainThread = listenerMainThread; } public synchronized boolean isCompleted() { return countOperationsEnqueued == countOperationsCompleted; } /** * Waits until all enqueued operations are complete. If the thread gets interrupted, any * {@link InterruptedException} will be rethrown as a {@link DaoException}. */ public synchronized void waitForCompletion() { while (!isCompleted()) { try { wait(); } catch (InterruptedException e) { throw new DaoException("Interrupted while waiting for all operations to complete", e); } } } /** * Waits until all enqueued operations are complete, but at most the given amount of milliseconds. If the thread * gets interrupted, any {@link InterruptedException} will be rethrown as a {@link DaoException}. * * @return true if operations completed in the given time frame. */ public synchronized boolean waitForCompletion(int maxMillis) { if (!isCompleted()) { try { wait(maxMillis); } catch (InterruptedException e) { throw new DaoException("Interrupted while waiting for all operations to complete", e); } } return isCompleted(); } @Override public void run() { try { try { while (true) { AsyncOperation operation = queue.poll(1, TimeUnit.SECONDS); if (operation == null) { synchronized (this) { // Check again, this time in synchronized to be in sync with enqueue(AsyncOperation) operation = queue.poll(); if (operation == null) { // set flag while still inside synchronized executorRunning = false; return; } } } if (operation.isMergeTx()) { // Wait some ms for another operation to merge because a TX is expensive AsyncOperation operation2 = queue.poll(waitForMergeMillis, TimeUnit.MILLISECONDS); if (operation2 != null) { if (operation.isMergeableWith(operation2)) { mergeTxAndExecute(operation, operation2); } else { // Cannot merge, execute both executeOperationAndPostCompleted(operation); executeOperationAndPostCompleted(operation2); } continue; } } executeOperationAndPostCompleted(operation); } } catch (InterruptedException e) { DaoLog.w(Thread.currentThread().getName() + " was interruppted", e); } } finally { executorRunning = false; } } /** Also checks for other operations in the queue that can be merged into the transaction. */ private void mergeTxAndExecute(AsyncOperation operation1, AsyncOperation operation2) { ArrayList mergedOps = new ArrayList(); mergedOps.add(operation1); mergedOps.add(operation2); Database db = operation1.getDatabase(); db.beginTransaction(); boolean success = false; try { for (int i = 0; i < mergedOps.size(); i++) { AsyncOperation operation = mergedOps.get(i); executeOperation(operation); if (operation.isFailed()) { // Operation may still have changed the DB, roll back everything break; } if (i == mergedOps.size() - 1) { AsyncOperation peekedOp = queue.peek(); if (i < maxOperationCountToMerge && operation.isMergeableWith(peekedOp)) { AsyncOperation removedOp = queue.remove(); if (removedOp != peekedOp) { // Paranoia check, should not occur unless threading is broken throw new DaoException("Internal error: peeked op did not match removed op"); } mergedOps.add(removedOp); } else { // No more ops in the queue to merge, finish it db.setTransactionSuccessful(); success = true; break; } } } } finally { try { db.endTransaction(); } catch (RuntimeException e) { DaoLog.i("Async transaction could not be ended, success so far was: " + success, e); success = false; } } if (success) { int mergedCount = mergedOps.size(); for (AsyncOperation asyncOperation : mergedOps) { asyncOperation.mergedOperationsCount = mergedCount; handleOperationCompleted(asyncOperation); } } else { DaoLog.i("Reverted merged transaction because one of the operations failed. Executing operations one by " + "one instead..."); for (AsyncOperation asyncOperation : mergedOps) { asyncOperation.reset(); executeOperationAndPostCompleted(asyncOperation); } } } private void handleOperationCompleted(AsyncOperation operation) { operation.setCompleted(); AsyncOperationListener listenerToCall = listener; if (listenerToCall != null) { listenerToCall.onAsyncOperationCompleted(operation); } if (listenerMainThread != null) { if (handlerMainThread == null) { handlerMainThread = new Handler(Looper.getMainLooper(), this); } Message msg = handlerMainThread.obtainMessage(1, operation); handlerMainThread.sendMessage(msg); } synchronized (this) { countOperationsCompleted++; if (countOperationsCompleted == countOperationsEnqueued) { notifyAll(); } } } private void executeOperationAndPostCompleted(AsyncOperation operation) { executeOperation(operation); handleOperationCompleted(operation); } @SuppressWarnings({"unchecked", "rawtypes"}) private void executeOperation(AsyncOperation operation) { operation.timeStarted = System.currentTimeMillis(); try { switch (operation.type) { case Delete: operation.dao.delete(operation.parameter); break; case DeleteInTxIterable: operation.dao.deleteInTx((Iterable) operation.parameter); break; case DeleteInTxArray: operation.dao.deleteInTx((Object[]) operation.parameter); break; case Insert: operation.dao.insert(operation.parameter); break; case InsertInTxIterable: operation.dao.insertInTx((Iterable) operation.parameter); break; case InsertInTxArray: operation.dao.insertInTx((Object[]) operation.parameter); break; case InsertOrReplace: operation.dao.insertOrReplace(operation.parameter); break; case InsertOrReplaceInTxIterable: operation.dao.insertOrReplaceInTx((Iterable) operation.parameter); break; case InsertOrReplaceInTxArray: operation.dao.insertOrReplaceInTx((Object[]) operation.parameter); break; case Update: operation.dao.update(operation.parameter); break; case UpdateInTxIterable: operation.dao.updateInTx((Iterable) operation.parameter); break; case UpdateInTxArray: operation.dao.updateInTx((Object[]) operation.parameter); break; case TransactionRunnable: executeTransactionRunnable(operation); break; case TransactionCallable: executeTransactionCallable(operation); break; case QueryList: operation.result = ((Query) operation.parameter).forCurrentThread().list(); break; case QueryUnique: operation.result = ((Query) operation.parameter).forCurrentThread().unique(); break; case DeleteByKey: operation.dao.deleteByKey(operation.parameter); break; case DeleteAll: operation.dao.deleteAll(); break; case Load: operation.result = operation.dao.load(operation.parameter); break; case LoadAll: operation.result = operation.dao.loadAll(); break; case Count: operation.result = operation.dao.count(); break; case Refresh: operation.dao.refresh(operation.parameter); break; default: throw new DaoException("Unsupported operation: " + operation.type); } } catch (Throwable th) { operation.throwable = th; } operation.timeCompleted = System.currentTimeMillis(); // Do not set it to completed here because it might be a merged TX } private void executeTransactionRunnable(AsyncOperation operation) { Database db = operation.getDatabase(); db.beginTransaction(); try { ((Runnable) operation.parameter).run(); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } @SuppressWarnings("unchecked") private void executeTransactionCallable(AsyncOperation operation) throws Exception { Database db = operation.getDatabase(); db.beginTransaction(); try { operation.result = ((Callable) operation.parameter).call(); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } @Override public boolean handleMessage(Message msg) { AsyncOperationListener listenerToCall = listenerMainThread; if (listenerToCall != null) { listenerToCall.onAsyncOperationCompleted((AsyncOperation) msg.obj); } return false; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/async/AsyncOperationListener.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.async; /** Listener being called after completion of {@link org.greenrobot.greendao.async.AsyncOperation}. */ public interface AsyncOperationListener { /** * Note, that the operation may not have been successful, check * {@link AsyncOperation#isFailed()} and/or {@link AsyncOperation#getThrowable()} for error situations. */ void onAsyncOperationCompleted(AsyncOperation operation); } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/async/AsyncSession.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.async; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.AbstractDaoSession; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.async.AsyncOperation.OperationType; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.query.Query; import java.util.concurrent.Callable; /** * Asynchronous interface to entity operations. All operations will enqueued a @link {@link AsyncOperation} and return * immediately (fine to call on the UI/main thread). The queue will be processed in a (single) background thread. The * processing order is the call order of the operations. It's possible to start multiple AsyncSessions that will * execute * concurrently. * * @author Markus * @see AbstractDaoSession#startAsyncSession() */ // Facade to AsyncOperationExecutor: prepares operations and delegates work to AsyncOperationExecutor. public class AsyncSession { private final AbstractDaoSession daoSession; private final AsyncOperationExecutor executor; private int sessionFlags; public AsyncSession(AbstractDaoSession daoSession) { this.daoSession = daoSession; this.executor = new AsyncOperationExecutor(); } public int getMaxOperationCountToMerge() { return executor.getMaxOperationCountToMerge(); } public void setMaxOperationCountToMerge(int maxOperationCountToMerge) { executor.setMaxOperationCountToMerge(maxOperationCountToMerge); } public int getWaitForMergeMillis() { return executor.getWaitForMergeMillis(); } public void setWaitForMergeMillis(int waitForMergeMillis) { executor.setWaitForMergeMillis(waitForMergeMillis); } public AsyncOperationListener getListener() { return executor.getListener(); } public void setListener(AsyncOperationListener listener) { executor.setListener(listener); } public AsyncOperationListener getListenerMainThread() { return executor.getListenerMainThread(); } public void setListenerMainThread(AsyncOperationListener listenerMainThread) { executor.setListenerMainThread(listenerMainThread); } public boolean isCompleted() { return executor.isCompleted(); } /** * Waits until all enqueued operations are complete. If the thread gets interrupted, any * {@link InterruptedException} will be rethrown as a {@link DaoException}. */ public void waitForCompletion() { executor.waitForCompletion(); } /** * Waits until all enqueued operations are complete, but at most the given amount of milliseconds. If the thread * gets interrupted, any {@link InterruptedException} will be rethrown as a {@link DaoException}. * * @return true if operations completed in the given time frame. */ public boolean waitForCompletion(int maxMillis) { return executor.waitForCompletion(maxMillis); } /** Asynchronous version of {@link AbstractDao#insert(Object)}. */ public AsyncOperation insert(Object entity) { return insert(entity, 0); } /** Asynchronous version of {@link AbstractDao#insert(Object)}. */ public AsyncOperation insert(Object entity, int flags) { return enqueueEntityOperation(OperationType.Insert, entity, flags); } /** Asynchronous version of {@link AbstractDao#insertInTx(Object...)}. */ public AsyncOperation insertInTx(Class entityClass, E... entities) { return insertInTx(entityClass, 0, entities); } /** Asynchronous version of {@link AbstractDao#insertInTx(Object...)}. */ public AsyncOperation insertInTx(Class entityClass, int flags, E... entities) { return enqueEntityOperation(OperationType.InsertInTxArray, entityClass, entities, flags); } /** Asynchronous version of {@link AbstractDao#insertInTx(Iterable)}. */ public AsyncOperation insertInTx(Class entityClass, Iterable entities) { return insertInTx(entityClass, entities, 0); } /** Asynchronous version of {@link AbstractDao#insertInTx(Iterable)}. */ public AsyncOperation insertInTx(Class entityClass, Iterable entities, int flags) { return enqueEntityOperation(OperationType.InsertInTxIterable, entityClass, entities, flags); } /** Asynchronous version of {@link AbstractDao#insertOrReplace(Object)}. */ public AsyncOperation insertOrReplace(Object entity) { return insertOrReplace(entity, 0); } /** Asynchronous version of {@link AbstractDao#insertOrReplace(Object)}. */ public AsyncOperation insertOrReplace(Object entity, int flags) { return enqueueEntityOperation(OperationType.InsertOrReplace, entity, flags); } /** Asynchronous version of {@link AbstractDao#insertOrReplaceInTx(Object...)}. */ public AsyncOperation insertOrReplaceInTx(Class entityClass, E... entities) { return insertOrReplaceInTx(entityClass, 0, entities); } /** Asynchronous version of {@link AbstractDao#insertOrReplaceInTx(Object...)}. */ public AsyncOperation insertOrReplaceInTx(Class entityClass, int flags, E... entities) { return enqueEntityOperation(OperationType.InsertOrReplaceInTxArray, entityClass, entities, flags); } /** Asynchronous version of {@link AbstractDao#insertOrReplaceInTx(Iterable)}. */ public AsyncOperation insertOrReplaceInTx(Class entityClass, Iterable entities) { return insertOrReplaceInTx(entityClass, entities, 0); } /** Asynchronous version of {@link AbstractDao#insertOrReplaceInTx(Iterable)}. */ public AsyncOperation insertOrReplaceInTx(Class entityClass, Iterable entities, int flags) { return enqueEntityOperation(OperationType.InsertOrReplaceInTxIterable, entityClass, entities, flags); } /** Asynchronous version of {@link AbstractDao#update(Object)}. */ public AsyncOperation update(Object entity) { return update(entity, 0); } /** Asynchronous version of {@link AbstractDao#update(Object)}. */ public AsyncOperation update(Object entity, int flags) { return enqueueEntityOperation(OperationType.Update, entity, flags); } /** Asynchronous version of {@link AbstractDao#updateInTx(Object...)}. */ public AsyncOperation updateInTx(Class entityClass, E... entities) { return updateInTx(entityClass, 0, entities); } /** Asynchronous version of {@link AbstractDao#updateInTx(Object...)}. */ public AsyncOperation updateInTx(Class entityClass, int flags, E... entities) { return enqueEntityOperation(OperationType.UpdateInTxArray, entityClass, entities, flags); } /** Asynchronous version of {@link AbstractDao#updateInTx(Iterable)}. */ public AsyncOperation updateInTx(Class entityClass, Iterable entities) { return updateInTx(entityClass, entities, 0); } /** Asynchronous version of {@link AbstractDao#updateInTx(Iterable)}. */ public AsyncOperation updateInTx(Class entityClass, Iterable entities, int flags) { return enqueEntityOperation(OperationType.UpdateInTxIterable, entityClass, entities, flags); } /** Asynchronous version of {@link AbstractDao#delete(Object)}. */ public AsyncOperation delete(Object entity) { return delete(entity, 0); } /** Asynchronous version of {@link AbstractDao#delete(Object)}. */ public AsyncOperation delete(Object entity, int flags) { return enqueueEntityOperation(OperationType.Delete, entity, flags); } /** Asynchronous version of {@link AbstractDao#deleteByKey(Object)}. */ public AsyncOperation deleteByKey(Object key) { return deleteByKey(key, 0); } /** Asynchronous version of {@link AbstractDao#deleteByKey(Object)}. */ public AsyncOperation deleteByKey(Object key, int flags) { return enqueueEntityOperation(OperationType.DeleteByKey, key, flags); } /** Asynchronous version of {@link AbstractDao#deleteInTx(Object...)}. */ public AsyncOperation deleteInTx(Class entityClass, E... entities) { return deleteInTx(entityClass, 0, entities); } /** Asynchronous version of {@link AbstractDao#deleteInTx(Object...)}. */ public AsyncOperation deleteInTx(Class entityClass, int flags, E... entities) { return enqueEntityOperation(OperationType.DeleteInTxArray, entityClass, entities, flags); } /** Asynchronous version of {@link AbstractDao#deleteInTx(Iterable)}. */ public AsyncOperation deleteInTx(Class entityClass, Iterable entities) { return deleteInTx(entityClass, entities, 0); } /** Asynchronous version of {@link AbstractDao#deleteInTx(Iterable)}. */ public AsyncOperation deleteInTx(Class entityClass, Iterable entities, int flags) { return enqueEntityOperation(OperationType.DeleteInTxIterable, entityClass, entities, flags); } /** Asynchronous version of {@link AbstractDao#deleteAll()}. */ public AsyncOperation deleteAll(Class entityClass) { return deleteAll(entityClass, 0); } /** Asynchronous version of {@link AbstractDao#deleteAll()}. */ public AsyncOperation deleteAll(Class entityClass, int flags) { return enqueEntityOperation(OperationType.DeleteAll, entityClass, null, flags); } /** Asynchronous version of {@link AbstractDaoSession#runInTx(Runnable)}. */ public AsyncOperation runInTx(Runnable runnable) { return runInTx(runnable, 0); } /** Asynchronous version of {@link AbstractDaoSession#runInTx(Runnable)}. */ public AsyncOperation runInTx(Runnable runnable, int flags) { return enqueueDatabaseOperation(OperationType.TransactionRunnable, runnable, flags); } /** Asynchronous version of {@link AbstractDaoSession#callInTx(Callable)}. */ public AsyncOperation callInTx(Callable callable) { return callInTx(callable, 0); } /** Asynchronous version of {@link AbstractDaoSession#callInTx(Callable)}. */ public AsyncOperation callInTx(Callable callable, int flags) { return enqueueDatabaseOperation(OperationType.TransactionCallable, callable, flags); } /** Asynchronous version of {@link Query#list()}. */ public AsyncOperation queryList(Query query) { return queryList(query, 0); } /** Asynchronous version of {@link Query#list()}. */ public AsyncOperation queryList(Query query, int flags) { return enqueueDatabaseOperation(OperationType.QueryList, query, flags); } /** Asynchronous version of {@link Query#unique()}. */ public AsyncOperation queryUnique(Query query) { return queryUnique(query, 0); } /** Asynchronous version of {@link Query#unique()}. */ public AsyncOperation queryUnique(Query query, int flags) { return enqueueDatabaseOperation(OperationType.QueryUnique, query, flags); } /** Asynchronous version of {@link AbstractDao#load(Object)}. */ public AsyncOperation load(Class entityClass, Object key) { return load(entityClass, key, 0); } /** Asynchronous version of {@link AbstractDao#load(Object)}. */ public AsyncOperation load(Class entityClass, Object key, int flags) { return enqueEntityOperation(OperationType.Load, entityClass, key, flags); } /** Asynchronous version of {@link AbstractDao#loadAll()}. */ public AsyncOperation loadAll(Class entityClass) { return loadAll(entityClass, 0); } /** Asynchronous version of {@link AbstractDao#loadAll()}. */ public AsyncOperation loadAll(Class entityClass, int flags) { return enqueEntityOperation(OperationType.LoadAll, entityClass, null, flags); } /** Asynchronous version of {@link AbstractDao#count()}. */ public AsyncOperation count(Class entityClass) { return count(entityClass, 0); } /** Asynchronous version of {@link AbstractDao#count()}. */ public AsyncOperation count(Class entityClass, int flags) { return enqueEntityOperation(OperationType.Count, entityClass, null, flags); } /** Asynchronous version of {@link AbstractDao#refresh(Object)}. */ public AsyncOperation refresh(Object entity) { return refresh(entity, 0); } /** Asynchronous version of {@link AbstractDao#refresh(Object)}. */ public AsyncOperation refresh(Object entity, int flags) { return enqueueEntityOperation(OperationType.Refresh, entity, flags); } private AsyncOperation enqueueDatabaseOperation(OperationType type, Object param, int flags) { Database database = daoSession.getDatabase(); AsyncOperation operation = new AsyncOperation(type, null, database, param, flags | sessionFlags); executor.enqueue(operation); return operation; } private AsyncOperation enqueueEntityOperation(OperationType type, Object entity, int flags) { return enqueEntityOperation(type, entity.getClass(), entity, flags); } private AsyncOperation enqueEntityOperation(OperationType type, Class entityClass, Object param, int flags) { AbstractDao dao = daoSession.getDao(entityClass); AsyncOperation operation = new AsyncOperation(type, dao, null, param, flags | sessionFlags); executor.enqueue(operation); return operation; } /** {@link org.greenrobot.greendao.async.AsyncOperation} flags set for all operations (will be ORed with call flags). */ public int getSessionFlags() { return sessionFlags; } /** {@link org.greenrobot.greendao.async.AsyncOperation} flags set for all operations (will be ORed with call flags). */ public void setSessionFlags(int sessionFlags) { this.sessionFlags = sessionFlags; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/database/Database.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.database; import android.database.Cursor; import android.database.SQLException; /** * Database abstraction used internally by greenDAO. */ public interface Database { Cursor rawQuery(String sql, String[] selectionArgs); void execSQL(String sql) throws SQLException; void beginTransaction(); void endTransaction(); boolean inTransaction(); void setTransactionSuccessful(); void execSQL(String sql, Object[] bindArgs) throws SQLException; DatabaseStatement compileStatement(String sql); boolean isDbLockedByCurrentThread(); boolean isOpen(); void close(); Object getRawDatabase(); } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/database/DatabaseOpenHelper.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.database; import android.annotation.SuppressLint; import android.content.Context; import android.database.DatabaseErrorHandler; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import org.greenrobot.greendao.DaoException; import java.lang.reflect.Constructor; /** * SQLiteOpenHelper to allow working with greenDAO's {@link Database} abstraction to create and update database schemas. */ public abstract class DatabaseOpenHelper extends SQLiteOpenHelper { private final Context context; private final String name; private final int version; private EncryptedHelper encryptedHelper; private boolean loadSQLCipherNativeLibs = true; public DatabaseOpenHelper(Context context, String name, int version) { this(context, name, null, version); } public DatabaseOpenHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); this.context = context; this.name = name; this.version = version; } @SuppressLint("NewApi") public DatabaseOpenHelper(Context context, String name, CursorFactory factory, int version, DatabaseErrorHandler errorHandler) { super(context, name, factory, version, errorHandler); this.context = context; this.name = name; this.version = version; } /** * Flag to load SQLCipher native libs (default: true). */ public void setLoadSQLCipherNativeLibs(boolean loadSQLCipherNativeLibs) { this.loadSQLCipherNativeLibs = loadSQLCipherNativeLibs; } /** * Like {@link #getWritableDatabase()}, but returns a greenDAO abstraction of the database. * The backing DB is an standard {@link SQLiteDatabase}. */ public Database getWritableDb() { return wrap(getWritableDatabase()); } /** * Like {@link #getReadableDatabase()}, but returns a greenDAO abstraction of the database. * The backing DB is an standard {@link SQLiteDatabase}. */ public Database getReadableDb() { return wrap(getReadableDatabase()); } protected Database wrap(SQLiteDatabase sqLiteDatabase) { return new StandardDatabase(sqLiteDatabase); } /** * Delegates to {@link #onCreate(Database)}, which uses greenDAO's database abstraction. */ @Override public void onCreate(SQLiteDatabase db) { onCreate(wrap(db)); } /** * Override this if you do not want to depend on {@link SQLiteDatabase}. */ public void onCreate(Database db) { // Do nothing by default } /** * Delegates to {@link #onUpgrade(Database, int, int)}, which uses greenDAO's database abstraction. */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(wrap(db), oldVersion, newVersion); } /** * Override this if you do not want to depend on {@link SQLiteDatabase}. */ public void onUpgrade(Database db, int oldVersion, int newVersion) { // Do nothing by default } /** * Delegates to {@link #onOpen(Database)}, which uses greenDAO's database abstraction. */ @Override public void onOpen(SQLiteDatabase db) { onOpen(wrap(db)); } /** * Override this if you do not want to depend on {@link SQLiteDatabase}. */ public void onOpen(Database db) { // Do nothing by default } interface EncryptedHelper { Database getEncryptedReadableDb(String password); Database getEncryptedReadableDb(char[] password); Database getEncryptedWritableDb(String password); Database getEncryptedWritableDb(char[] password); } private EncryptedHelper checkEncryptedHelper() { if (encryptedHelper == null) { try { Class.forName("net.sqlcipher.database.SQLiteOpenHelper"); } catch (ClassNotFoundException e) { throw new DaoException("Using an encrypted database requires SQLCipher, " + "make sure to add it to dependencies: " + "https://greenrobot.org/greendao/documentation/database-encryption/"); } // Avoid referencing SqlCipherEncryptedHelper to avoid // "Rejecting re-init on previously-failed class java.lang.NoClassDefFoundError" logs // if SQLCipher is not in classpath. try { Class helperClass = Class.forName( "org.greenrobot.greendao.database.SqlCipherEncryptedHelper"); Constructor constructor = helperClass.getConstructor( DatabaseOpenHelper.class, Context.class, String.class, int.class, boolean.class); encryptedHelper = (EncryptedHelper) constructor.newInstance( this, context, name, version, loadSQLCipherNativeLibs); } catch (Exception e) { throw new DaoException(e); } } return encryptedHelper; } /** * Use this to initialize an encrypted SQLCipher database. * * @see #onCreate(Database) * @see #onUpgrade(Database, int, int) */ public Database getEncryptedWritableDb(String password) { EncryptedHelper encryptedHelper = checkEncryptedHelper(); return encryptedHelper.getEncryptedWritableDb(password); } /** * Use this to initialize an encrypted SQLCipher database. * * @see #onCreate(Database) * @see #onUpgrade(Database, int, int) */ public Database getEncryptedWritableDb(char[] password) { EncryptedHelper encryptedHelper = checkEncryptedHelper(); return encryptedHelper.getEncryptedWritableDb(password); } /** * Use this to initialize an encrypted SQLCipher database. * * @see #onCreate(Database) * @see #onUpgrade(Database, int, int) */ public Database getEncryptedReadableDb(String password) { EncryptedHelper encryptedHelper = checkEncryptedHelper(); return encryptedHelper.getEncryptedReadableDb(password); } /** * Use this to initialize an encrypted SQLCipher database. * * @see #onCreate(Database) * @see #onUpgrade(Database, int, int) */ public Database getEncryptedReadableDb(char[] password) { EncryptedHelper encryptedHelper = checkEncryptedHelper(); return encryptedHelper.getEncryptedReadableDb(password); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/database/DatabaseStatement.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.database; public interface DatabaseStatement { void execute(); long simpleQueryForLong(); void bindNull(int index); long executeInsert(); void bindString(int index, String value); void bindBlob(int index, byte[] value); void bindLong(int index, long value); void clearBindings(); void bindDouble(int index, double value); void close(); Object getRawStatement(); } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/database/EncryptedDatabase.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.database; import android.database.Cursor; import android.database.SQLException; import net.sqlcipher.database.SQLiteDatabase; public class EncryptedDatabase implements Database { private final SQLiteDatabase delegate; public EncryptedDatabase(SQLiteDatabase delegate) { this.delegate = delegate; } @Override public Cursor rawQuery(String sql, String[] selectionArgs) { return delegate.rawQuery(sql, selectionArgs); } @Override public void execSQL(String sql) throws SQLException { delegate.execSQL(sql); } @Override public void beginTransaction() { delegate.beginTransaction(); } @Override public void endTransaction() { delegate.endTransaction(); } @Override public boolean inTransaction() { return delegate.inTransaction(); } @Override public void setTransactionSuccessful() { delegate.setTransactionSuccessful(); } @Override public void execSQL(String sql, Object[] bindArgs) throws SQLException { delegate.execSQL(sql, bindArgs); } @Override public DatabaseStatement compileStatement(String sql) { return new EncryptedDatabaseStatement(delegate.compileStatement(sql)); } @Override public boolean isDbLockedByCurrentThread() { return delegate.isDbLockedByCurrentThread(); } @Override public boolean isOpen() { return delegate.isOpen(); } @Override public void close() { delegate.close(); } @Override public Object getRawDatabase() { return delegate; } public SQLiteDatabase getSQLiteDatabase() { return delegate; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/database/EncryptedDatabaseStatement.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.database; import net.sqlcipher.database.SQLiteStatement; public class EncryptedDatabaseStatement implements DatabaseStatement { private final SQLiteStatement delegate; public EncryptedDatabaseStatement(SQLiteStatement delegate) { this.delegate = delegate; } @Override public void execute() { delegate.execute(); } @Override public long simpleQueryForLong() { return delegate.simpleQueryForLong(); } @Override public void bindNull(int index) { delegate.bindNull(index); } @Override public long executeInsert() { return delegate.executeInsert(); } @Override public void bindString(int index, String value) { delegate.bindString(index, value); } @Override public void bindBlob(int index, byte[] value) { delegate.bindBlob(index, value); } @Override public void bindLong(int index, long value) { delegate.bindLong(index, value); } @Override public void clearBindings() { delegate.clearBindings(); } @Override public void bindDouble(int index, double value) { delegate.bindDouble(index, value); } @Override public void close() { delegate.close(); } @Override public Object getRawStatement() { return delegate; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/database/SqlCipherEncryptedHelper.java ================================================ package org.greenrobot.greendao.database; import android.content.Context; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteOpenHelper; class SqlCipherEncryptedHelper extends SQLiteOpenHelper implements DatabaseOpenHelper.EncryptedHelper { private final DatabaseOpenHelper delegate; public SqlCipherEncryptedHelper(DatabaseOpenHelper delegate, Context context, String name, int version, boolean loadLibs) { super(context, name, null, version); this.delegate = delegate; if (loadLibs) { SQLiteDatabase.loadLibs(context); } } private Database wrap(SQLiteDatabase sqLiteDatabase) { return new EncryptedDatabase(sqLiteDatabase); } @Override public void onCreate(SQLiteDatabase db) { delegate.onCreate(wrap(db)); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { delegate.onUpgrade(wrap(db), oldVersion, newVersion); } @Override public void onOpen(SQLiteDatabase db) { delegate.onOpen(wrap(db)); } @Override public Database getEncryptedReadableDb(String password) { return wrap(getReadableDatabase(password)); } @Override public Database getEncryptedReadableDb(char[] password) { return wrap(getReadableDatabase(password)); } @Override public Database getEncryptedWritableDb(String password) { return wrap(getWritableDatabase(password)); } @Override public Database getEncryptedWritableDb(char[] password) { return wrap(getWritableDatabase(password)); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/database/StandardDatabase.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.database; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; public class StandardDatabase implements Database { private final SQLiteDatabase delegate; public StandardDatabase(SQLiteDatabase delegate) { this.delegate = delegate; } @Override public Cursor rawQuery(String sql, String[] selectionArgs) { return delegate.rawQuery(sql, selectionArgs); } @Override public void execSQL(String sql) throws SQLException { delegate.execSQL(sql); } @Override public void beginTransaction() { delegate.beginTransaction(); } @Override public void endTransaction() { delegate.endTransaction(); } @Override public boolean inTransaction() { return delegate.inTransaction(); } @Override public void setTransactionSuccessful() { delegate.setTransactionSuccessful(); } @Override public void execSQL(String sql, Object[] bindArgs) throws SQLException { delegate.execSQL(sql, bindArgs); } @Override public DatabaseStatement compileStatement(String sql) { return new StandardDatabaseStatement(delegate.compileStatement(sql)); } @Override public boolean isDbLockedByCurrentThread() { return delegate.isDbLockedByCurrentThread(); } @Override public boolean isOpen() { return delegate.isOpen(); } @Override public void close() { delegate.close(); } @Override public Object getRawDatabase() { return delegate; } public SQLiteDatabase getSQLiteDatabase() { return delegate; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/database/StandardDatabaseStatement.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.database; import android.database.sqlite.SQLiteStatement; public class StandardDatabaseStatement implements DatabaseStatement { private final SQLiteStatement delegate; public StandardDatabaseStatement(SQLiteStatement delegate) { this.delegate = delegate; } @Override public void execute() { delegate.execute(); } @Override public long simpleQueryForLong() { return delegate.simpleQueryForLong(); } @Override public void bindNull(int index) { delegate.bindNull(index); } @Override public long executeInsert() { return delegate.executeInsert(); } @Override public void bindString(int index, String value) { delegate.bindString(index, value); } @Override public void bindBlob(int index, byte[] value) { delegate.bindBlob(index, value); } @Override public void bindLong(int index, long value) { delegate.bindLong(index, value); } @Override public void clearBindings() { delegate.clearBindings(); } @Override public void bindDouble(int index, double value) { delegate.bindDouble(index, value); } @Override public void close() { delegate.close(); } @Override public Object getRawStatement() { return delegate; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/identityscope/IdentityScope.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.identityscope; /** * Common interface for a identity scopes needed internally by greenDAO. Identity scopes let greenDAO re-use Java * objects. * * @author Markus * * @param * Key * @param * Entity */ public interface IdentityScope { T get(K key); void put(K key, T entity); T getNoLock(K key); void putNoLock(K key, T entity); boolean detach(K key, T entity); void remove(K key); void remove(Iterable key); void clear(); void lock(); void unlock(); void reserveRoom(int count); } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/identityscope/IdentityScopeLong.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.identityscope; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.concurrent.locks.ReentrantLock; import org.greenrobot.greendao.internal.LongHashMap; /** * The context for entity identities. Provides the scope in which entities will be tracked and managed. * * @author Markus * @param * Entity */ public class IdentityScopeLong implements IdentityScope { private final LongHashMap> map; private final ReentrantLock lock; public IdentityScopeLong() { map = new LongHashMap>(); lock = new ReentrantLock(); } @Override public T get(Long key) { return get2(key); } @Override public T getNoLock(Long key) { return get2NoLock(key); } public T get2(long key) { lock.lock(); Reference ref; try { ref = map.get(key); } finally { lock.unlock(); } if (ref != null) { return ref.get(); } else { return null; } } public T get2NoLock(long key) { Reference ref = map.get(key); if (ref != null) { return ref.get(); } else { return null; } } @Override public void put(Long key, T entity) { put2(key, entity); } @Override public void putNoLock(Long key, T entity) { put2NoLock(key, entity); } public void put2(long key, T entity) { lock.lock(); try { map.put(key, new WeakReference(entity)); } finally { lock.unlock(); } } public void put2NoLock(long key, T entity) { map.put(key, new WeakReference(entity)); } @Override public boolean detach(Long key, T entity) { lock.lock(); try { if (get(key) == entity && entity != null) { remove(key); return true; } else { return false; } } finally { lock.unlock(); } } @Override public void remove(Long key) { lock.lock(); try { map.remove(key); } finally { lock.unlock(); } } @Override public void remove(Iterable keys) { lock.lock(); try { for (Long key : keys) { map.remove(key); } } finally { lock.unlock(); } } @Override public void clear() { lock.lock(); try { map.clear(); } finally { lock.unlock(); } } @Override public void lock() { lock.lock(); } @Override public void unlock() { lock.unlock(); } @Override public void reserveRoom(int count) { map.reserveRoom(count); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/identityscope/IdentityScopeObject.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.identityscope; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.concurrent.locks.ReentrantLock; /** * The context for entity identities. Provides the scope in which entities will be tracked and managed. * * @author Markus * @param * @param */ public class IdentityScopeObject implements IdentityScope { private final HashMap> map; private final ReentrantLock lock; public IdentityScopeObject() { map = new HashMap>(); lock = new ReentrantLock(); } @Override public T get(K key) { Reference ref; lock.lock(); try { ref = map.get(key); } finally { lock.unlock(); } if (ref != null) { return ref.get(); } else { return null; } } @Override public T getNoLock(K key) { Reference ref = map.get(key); if (ref != null) { return ref.get(); } else { return null; } } @Override public void put(K key, T entity) { lock.lock(); try { map.put(key, new WeakReference(entity)); } finally { lock.unlock(); } } @Override public void putNoLock(K key, T entity) { map.put(key, new WeakReference(entity)); } @Override public boolean detach(K key, T entity) { lock.lock(); try { if (get(key) == entity && entity != null) { remove(key); return true; } else { return false; } } finally { lock.unlock(); } } @Override public void remove(K key) { lock.lock(); try { map.remove(key); } finally { lock.unlock(); } } @Override public void remove(Iterable< K> keys) { lock.lock(); try { for (K key : keys) { map.remove(key); } } finally { lock.unlock(); } } @Override public void clear() { lock.lock(); try { map.clear(); } finally { lock.unlock(); } } @Override public void lock() { lock.lock(); } @Override public void unlock() { lock.unlock(); } @Override public void reserveRoom(int count) { // HashMap does not allow } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/identityscope/IdentityScopeType.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.identityscope; public enum IdentityScopeType { Session, None } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/internal/DaoConfig.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.internal; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.Property; import org.greenrobot.greendao.identityscope.IdentityScope; import org.greenrobot.greendao.identityscope.IdentityScopeLong; import org.greenrobot.greendao.identityscope.IdentityScopeObject; import org.greenrobot.greendao.identityscope.IdentityScopeType; /** * Internal class used by greenDAO. DaoConfig stores essential data for DAOs, and is hold by AbstractDaoMaster. This * class will retrieve the required information from the DAO classes. */ public final class DaoConfig implements Cloneable { public final Database db; public final String tablename; public final Property[] properties; public final String[] allColumns; public final String[] pkColumns; public final String[] nonPkColumns; /** Single property PK or null if there's no PK or a multi property PK. */ public final Property pkProperty; public final boolean keyIsNumeric; public final TableStatements statements; private IdentityScope identityScope; public DaoConfig(Database db, Class> daoClass) { this.db = db; try { this.tablename = (String) daoClass.getField("TABLENAME").get(null); Property[] properties = reflectProperties(daoClass); this.properties = properties; allColumns = new String[properties.length]; List pkColumnList = new ArrayList(); List nonPkColumnList = new ArrayList(); Property lastPkProperty = null; for (int i = 0; i < properties.length; i++) { Property property = properties[i]; String name = property.columnName; allColumns[i] = name; if (property.primaryKey) { pkColumnList.add(name); lastPkProperty = property; } else { nonPkColumnList.add(name); } } String[] nonPkColumnsArray = new String[nonPkColumnList.size()]; nonPkColumns = nonPkColumnList.toArray(nonPkColumnsArray); String[] pkColumnsArray = new String[pkColumnList.size()]; pkColumns = pkColumnList.toArray(pkColumnsArray); pkProperty = pkColumns.length == 1 ? lastPkProperty : null; statements = new TableStatements(db, tablename, allColumns, pkColumns); if (pkProperty != null) { Class type = pkProperty.type; keyIsNumeric = type.equals(long.class) || type.equals(Long.class) || type.equals(int.class) || type.equals(Integer.class) || type.equals(short.class) || type.equals(Short.class) || type.equals(byte.class) || type.equals(Byte.class); } else { keyIsNumeric = false; } } catch (Exception e) { throw new DaoException("Could not init DAOConfig", e); } } private static Property[] reflectProperties(Class> daoClass) throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException { Class propertiesClass = Class.forName(daoClass.getName() + "$Properties"); Field[] fields = propertiesClass.getDeclaredFields(); ArrayList propertyList = new ArrayList(); final int modifierMask = Modifier.STATIC | Modifier.PUBLIC; for (Field field : fields) { // There might be other fields introduced by some tools, just ignore them (see issue #28) if ((field.getModifiers() & modifierMask) == modifierMask) { Object fieldValue = field.get(null); if (fieldValue instanceof Property) { propertyList.add((Property) fieldValue); } } } Property[] properties = new Property[propertyList.size()]; for (Property property : propertyList) { if (properties[property.ordinal] != null) { throw new DaoException("Duplicate property ordinals"); } properties[property.ordinal] = property; } return properties; } /** Does not copy identity scope. */ public DaoConfig(DaoConfig source) { db = source.db; tablename = source.tablename; properties = source.properties; allColumns = source.allColumns; pkColumns = source.pkColumns; nonPkColumns = source.nonPkColumns; pkProperty = source.pkProperty; statements = source.statements; keyIsNumeric = source.keyIsNumeric; } /** Does not copy identity scope. */ @Override public DaoConfig clone() { return new DaoConfig(this); } public IdentityScope getIdentityScope() { return identityScope; } /** * Clears the identify scope if it exists. */ public void clearIdentityScope() { IdentityScope identityScope = this.identityScope; if(identityScope != null) { identityScope.clear(); } } public void setIdentityScope(IdentityScope identityScope) { this.identityScope = identityScope; } @SuppressWarnings("rawtypes") public void initIdentityScope(IdentityScopeType type) { if (type == IdentityScopeType.None) { identityScope = null; } else if (type == IdentityScopeType.Session) { if (keyIsNumeric) { identityScope = new IdentityScopeLong(); } else { identityScope = new IdentityScopeObject(); } } else { throw new IllegalArgumentException("Unsupported type: " + type); } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/internal/FastCursor.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.internal; import android.content.ContentResolver; import android.database.CharArrayBuffer; import android.database.ContentObserver; import android.database.Cursor; import android.database.CursorWindow; import android.database.DataSetObserver; import android.net.Uri; import android.os.Bundle; /** Internal class used by greenDAO. */ final public class FastCursor implements Cursor { private final CursorWindow window; private int position; private final int count; public FastCursor(CursorWindow window) { this.window = window; count = window.getNumRows(); } @Override public int getCount() { return window.getNumRows(); } @Override public int getPosition() { return position; } @Override public boolean move(int offset) { return moveToPosition(position + offset); } @Override public boolean moveToPosition(int position) { if (position >= 0 && position < count) { this.position = position; return true; } else { return false; } } @Override public boolean moveToFirst() { position = 0; return count > 0; } @Override public boolean moveToLast() { if (count > 0) { position = count - 1; return true; } else { return false; } } @Override public boolean moveToNext() { if (position < count - 1) { position++; return true; } else { return false; } } @Override public boolean moveToPrevious() { if (position > 0) { position--; return true; } else { return false; } } @Override public boolean isFirst() { return position == 0; } @Override public boolean isLast() { return position == count - 1; } @Override public boolean isBeforeFirst() { throw new UnsupportedOperationException(); } @Override public boolean isAfterLast() { throw new UnsupportedOperationException(); } @Override public int getColumnIndex(String columnName) { throw new UnsupportedOperationException(); } @Override public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException { throw new UnsupportedOperationException(); } @Override public String getColumnName(int columnIndex) { throw new UnsupportedOperationException(); } @Override public String[] getColumnNames() { throw new UnsupportedOperationException(); } @Override public int getColumnCount() { throw new UnsupportedOperationException(); } @Override public byte[] getBlob(int columnIndex) { return window.getBlob(position, columnIndex); } @Override public String getString(int columnIndex) { return window.getString(position, columnIndex); } @Override public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { throw new UnsupportedOperationException(); } @Override public short getShort(int columnIndex) { return window.getShort(position, columnIndex); } @Override public int getInt(int columnIndex) { return window.getInt(position, columnIndex); } @Override public long getLong(int columnIndex) { return window.getLong(position, columnIndex); } @Override public float getFloat(int columnIndex) { return window.getFloat(position, columnIndex); } @Override public double getDouble(int columnIndex) { return window.getDouble(position, columnIndex); } @SuppressWarnings("deprecation") @Override public boolean isNull(int columnIndex) { return window.isNull(position, columnIndex); } @Override public void deactivate() { throw new UnsupportedOperationException(); } @Override public boolean requery() { throw new UnsupportedOperationException(); } @Override public void close() { throw new UnsupportedOperationException(); } @Override public boolean isClosed() { throw new UnsupportedOperationException(); } @Override public void registerContentObserver(ContentObserver observer) { throw new UnsupportedOperationException(); } @Override public void unregisterContentObserver(ContentObserver observer) { throw new UnsupportedOperationException(); } @Override public void registerDataSetObserver(DataSetObserver observer) { throw new UnsupportedOperationException(); } @Override public void unregisterDataSetObserver(DataSetObserver observer) { throw new UnsupportedOperationException(); } @Override public void setNotificationUri(ContentResolver cr, Uri uri) { throw new UnsupportedOperationException(); } @Override public boolean getWantsAllOnMoveCalls() { throw new UnsupportedOperationException(); } @Override public Bundle getExtras() { throw new UnsupportedOperationException(); } @Override public Bundle respond(Bundle extras) { throw new UnsupportedOperationException(); } /** Since API level 11 */ public int getType(int columnIndex) { throw new UnsupportedOperationException(); } /** Since API level 19 */ public Uri getNotificationUri() { return null; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/internal/LongHashMap.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.internal; import java.util.Arrays; import org.greenrobot.greendao.DaoLog; /** * An minimalistic hash map optimized for long keys. * * @author Markus * * @param * The class to store. */ public final class LongHashMap { final static class Entry { final long key; T value; Entry next; Entry(long key, T value, Entry next) { this.key = key; this.value = value; this.next = next; } } private Entry[] table; private int capacity; private int threshold; private int size; public LongHashMap() { this(16); } @SuppressWarnings("unchecked") public LongHashMap(int capacity) { this.capacity = capacity; this.threshold = capacity * 4 / 3; this.table = new Entry[capacity]; } public boolean containsKey(long key) { final int index = ((((int) (key >>> 32)) ^ ((int) (key))) & 0x7fffffff) % capacity; for (Entry entry = table[index]; entry != null; entry = entry.next) { if (entry.key == key) { return true; } } return false; } public T get(long key) { final int index = ((((int) (key >>> 32)) ^ ((int) (key))) & 0x7fffffff) % capacity; for (Entry entry = table[index]; entry != null; entry = entry.next) { if (entry.key == key) { return entry.value; } } return null; } public T put(long key, T value) { final int index = ((((int) (key >>> 32)) ^ ((int) (key))) & 0x7fffffff) % capacity; final Entry entryOriginal = table[index]; for (Entry entry = entryOriginal; entry != null; entry = entry.next) { if (entry.key == key) { T oldValue = entry.value; entry.value = value; return oldValue; } } table[index] = new Entry(key, value, entryOriginal); size++; if (size > threshold) { setCapacity(2 * capacity); } return null; } public T remove(long key) { int index = ((((int) (key >>> 32)) ^ ((int) (key))) & 0x7fffffff) % capacity; Entry previous = null; Entry entry = table[index]; while (entry != null) { Entry next = entry.next; if (entry.key == key) { if (previous == null) { table[index] = next; } else { previous.next = next; } size--; return entry.value; } previous = entry; entry = next; } return null; } public void clear() { size = 0; Arrays.fill(table, null); } public int size() { return size; } public void setCapacity(int newCapacity) { @SuppressWarnings("unchecked") Entry[] newTable = new Entry[newCapacity]; int length = table.length; for (int i = 0; i < length; i++) { Entry entry = table[i]; while (entry != null) { long key = entry.key; int index = ((((int) (key >>> 32)) ^ ((int) (key))) & 0x7fffffff) % newCapacity; Entry originalNext = entry.next; entry.next = newTable[index]; newTable[index] = entry; entry = originalNext; } } table = newTable; capacity = newCapacity; threshold = newCapacity * 4 / 3; } /** Target load: 0,6 */ public void reserveRoom(int entryCount) { setCapacity(entryCount * 5 / 3); } public void logStats() { int collisions = 0; for (Entry entry : table) { while (entry != null && entry.next != null) { collisions++; entry = entry.next; } } DaoLog.d("load: " + ((float) size) / capacity + ", size: " + size + ", capa: " + capacity + ", collisions: " + collisions + ", collision ratio: " + ((float) collisions) / size); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/internal/SqlUtils.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.internal; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.Property; /** Helper class to create SQL statements as used by greenDAO internally. */ public class SqlUtils { private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); public static StringBuilder appendProperty(StringBuilder builder, String tablePrefix, Property property) { if (tablePrefix != null) { builder.append(tablePrefix).append('.'); } builder.append('"').append(property.columnName).append('"'); return builder; } public static StringBuilder appendColumn(StringBuilder builder, String column) { builder.append('"').append(column).append('"'); return builder; } public static StringBuilder appendColumn(StringBuilder builder, String tableAlias, String column) { builder.append(tableAlias).append(".\"").append(column).append('"'); return builder; } public static StringBuilder appendColumns(StringBuilder builder, String tableAlias, String[] columns) { int length = columns.length; for (int i = 0; i < length; i++) { appendColumn(builder, tableAlias, columns[i]); if (i < length - 1) { builder.append(','); } } return builder; } public static StringBuilder appendColumns(StringBuilder builder, String[] columns) { int length = columns.length; for (int i = 0; i < length; i++) { builder.append('"').append(columns[i]).append('"'); if (i < length - 1) { builder.append(','); } } return builder; } public static StringBuilder appendPlaceholders(StringBuilder builder, int count) { for (int i = 0; i < count; i++) { if (i < count - 1) { builder.append("?,"); } else { builder.append('?'); } } return builder; } public static StringBuilder appendColumnsEqualPlaceholders(StringBuilder builder, String[] columns) { for (int i = 0; i < columns.length; i++) { appendColumn(builder, columns[i]).append("=?"); if (i < columns.length - 1) { builder.append(','); } } return builder; } public static StringBuilder appendColumnsEqValue(StringBuilder builder, String tableAlias, String[] columns) { for (int i = 0; i < columns.length; i++) { appendColumn(builder, tableAlias, columns[i]).append("=?"); if (i < columns.length - 1) { builder.append(','); } } return builder; } public static String createSqlInsert(String insertInto, String tablename, String[] columns) { StringBuilder builder = new StringBuilder(insertInto); builder.append('"').append(tablename).append('"').append(" ("); appendColumns(builder, columns); builder.append(") VALUES ("); appendPlaceholders(builder, columns.length); builder.append(')'); return builder.toString(); } /** Creates an select for given columns with a trailing space */ public static String createSqlSelect(String tablename, String tableAlias, String[] columns, boolean distinct) { if (tableAlias == null || tableAlias.length() < 0) { throw new DaoException("Table alias required"); } StringBuilder builder = new StringBuilder(distinct ? "SELECT DISTINCT " : "SELECT "); SqlUtils.appendColumns(builder, tableAlias, columns).append(" FROM "); builder.append('"').append(tablename).append('"').append(' ').append(tableAlias).append(' '); return builder.toString(); } /** Creates SELECT COUNT(*) with a trailing space. */ public static String createSqlSelectCountStar(String tablename, String tableAliasOrNull) { StringBuilder builder = new StringBuilder("SELECT COUNT(*) FROM "); builder.append('"').append(tablename).append('"').append(' '); if (tableAliasOrNull != null) { builder.append(tableAliasOrNull).append(' '); } return builder.toString(); } /** Remember: SQLite does not support joins nor table alias for DELETE. */ public static String createSqlDelete(String tablename, String[] columns) { String quotedTablename = '"' + tablename + '"'; StringBuilder builder = new StringBuilder("DELETE FROM "); builder.append(quotedTablename); if (columns != null && columns.length > 0) { builder.append(" WHERE "); appendColumnsEqValue(builder, quotedTablename, columns); } return builder.toString(); } public static String createSqlUpdate(String tablename, String[] updateColumns, String[] whereColumns) { String quotedTablename = '"' + tablename + '"'; StringBuilder builder = new StringBuilder("UPDATE "); builder.append(quotedTablename).append(" SET "); appendColumnsEqualPlaceholders(builder, updateColumns); builder.append(" WHERE "); appendColumnsEqValue(builder, quotedTablename, whereColumns); return builder.toString(); } public static String createSqlCount(String tablename) { return "SELECT COUNT(*) FROM \"" + tablename +'"'; } public static String escapeBlobArgument(byte[] bytes) { return "X'" + toHex(bytes) + '\''; } public static String toHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int i = 0; i < bytes.length; i++) { int byteValue = bytes[i] & 0xFF; hexChars[i * 2] = HEX_ARRAY[byteValue >>> 4]; hexChars[i * 2 + 1] = HEX_ARRAY[byteValue & 0x0F]; } return new String(hexChars); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/internal/TableStatements.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.internal; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.database.DatabaseStatement; /** Helper class to create SQL statements for specific tables (used by greenDAO internally). */ // Note: avoid locking while compiling any statement (accessing the db) to avoid deadlocks on lock-savvy DBs like // SQLCipher. public class TableStatements { private final Database db; private final String tablename; private final String[] allColumns; private final String[] pkColumns; private DatabaseStatement insertStatement; private DatabaseStatement insertOrReplaceStatement; private DatabaseStatement updateStatement; private DatabaseStatement deleteStatement; private DatabaseStatement countStatement; private volatile String selectAll; private volatile String selectByKey; private volatile String selectByRowId; private volatile String selectKeys; public TableStatements(Database db, String tablename, String[] allColumns, String[] pkColumns) { this.db = db; this.tablename = tablename; this.allColumns = allColumns; this.pkColumns = pkColumns; } public DatabaseStatement getInsertStatement() { if (insertStatement == null) { String sql = SqlUtils.createSqlInsert("INSERT INTO ", tablename, allColumns); DatabaseStatement newInsertStatement = db.compileStatement(sql); synchronized (this) { if (insertStatement == null) { insertStatement = newInsertStatement; } } if (insertStatement != newInsertStatement) { newInsertStatement.close(); } } return insertStatement; } public DatabaseStatement getInsertOrReplaceStatement() { if (insertOrReplaceStatement == null) { String sql = SqlUtils.createSqlInsert("INSERT OR REPLACE INTO ", tablename, allColumns); DatabaseStatement newInsertOrReplaceStatement = db.compileStatement(sql); synchronized (this) { if (insertOrReplaceStatement == null) { insertOrReplaceStatement = newInsertOrReplaceStatement; } } if (insertOrReplaceStatement != newInsertOrReplaceStatement) { newInsertOrReplaceStatement.close(); } } return insertOrReplaceStatement; } public DatabaseStatement getDeleteStatement() { if (deleteStatement == null) { String sql = SqlUtils.createSqlDelete(tablename, pkColumns); DatabaseStatement newDeleteStatement = db.compileStatement(sql); synchronized (this) { if (deleteStatement == null) { deleteStatement = newDeleteStatement; } } if (deleteStatement != newDeleteStatement) { newDeleteStatement.close(); } } return deleteStatement; } public DatabaseStatement getUpdateStatement() { if (updateStatement == null) { String sql = SqlUtils.createSqlUpdate(tablename, allColumns, pkColumns); DatabaseStatement newUpdateStatement = db.compileStatement(sql); synchronized (this) { if (updateStatement == null) { updateStatement = newUpdateStatement; } } if (updateStatement != newUpdateStatement) { newUpdateStatement.close(); } } return updateStatement; } public DatabaseStatement getCountStatement() { if (countStatement == null) { String sql = SqlUtils.createSqlCount(tablename); countStatement = db.compileStatement(sql); } return countStatement; } /** ends with an space to simplify appending to this string. */ public String getSelectAll() { if (selectAll == null) { selectAll = SqlUtils.createSqlSelect(tablename, "T", allColumns, false); } return selectAll; } /** ends with an space to simplify appending to this string. */ public String getSelectKeys() { if (selectKeys == null) { selectKeys = SqlUtils.createSqlSelect(tablename, "T", pkColumns, false); } return selectKeys; } // TODO precompile public String getSelectByKey() { if (selectByKey == null) { StringBuilder builder = new StringBuilder(getSelectAll()); builder.append("WHERE "); SqlUtils.appendColumnsEqValue(builder, "T", pkColumns); selectByKey = builder.toString(); } return selectByKey; } public String getSelectByRowId() { if (selectByRowId == null) { selectByRowId = getSelectAll() + "WHERE ROWID=?"; } return selectByRowId; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/AbstractQuery.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.InternalQueryDaoAccess; import java.util.Date; /** * A repeatable query returning entities. * * @author Markus * * @param * The entity class the query will return results for. */ // TODO support long, double and other types, not just Strings, for parameters // TODO Make parameters setable by Property (if unique in parameters) // TODO Make query compilable abstract class AbstractQuery { protected final AbstractDao dao; protected final InternalQueryDaoAccess daoAccess; protected final String sql; protected final String[] parameters; protected final Thread ownerThread; protected static String[] toStringArray(Object[] values) { int length = values.length; String[] strings = new String[length]; for (int i = 0; i < length; i++) { Object object = values[i]; if (object != null) { strings[i] = object.toString(); } else { strings[i] = null; } } return strings; } protected AbstractQuery(AbstractDao dao, String sql, String[] parameters) { this.dao = dao; this.daoAccess = new InternalQueryDaoAccess(dao); this.sql = sql; this.parameters = parameters; ownerThread = Thread.currentThread(); } // public void compile() { // // TODO implement compile // } /** * Sets the parameter (0 based) using the position in which it was added during building the query. */ public AbstractQuery setParameter(int index, Object parameter) { checkThread(); if (parameter != null) { parameters[index] = parameter.toString(); } else { parameters[index] = null; } return this; } /** * @see #setParameter(int, Object) */ public AbstractQuery setParameter(int index, Date parameter) { Long converted = parameter != null ? parameter.getTime() : null; return setParameter(index, converted); } /** * @see #setParameter(int, Object) */ public AbstractQuery setParameter(int index, Boolean parameter) { Integer converted = parameter != null ? (parameter ? 1 : 0) : null; return setParameter(index, converted); } protected void checkThread() { if (Thread.currentThread() != ownerThread) { throw new DaoException( "Method may be called only in owner thread, use forCurrentThread to get an instance for this thread"); } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/AbstractQueryData.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import org.greenrobot.greendao.AbstractDao; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; abstract class AbstractQueryData> { final String sql; final AbstractDao dao; final String[] initialValues; final Map> queriesForThreads; AbstractQueryData(AbstractDao dao, String sql, String[] initialValues) { this.dao = dao; this.sql = sql; this.initialValues = initialValues; queriesForThreads = new HashMap<>(); } /** * Just an optimized version, which performs faster if the current thread is already the query's owner thread. * Note: all parameters are reset to their initial values specified in {@link QueryBuilder}. */ Q forCurrentThread(Q query) { if (Thread.currentThread() == query.ownerThread) { System.arraycopy(initialValues, 0, query.parameters, 0, initialValues.length); return query; } else { return forCurrentThread(); } } /** * Note: all parameters are reset to their initial values specified in {@link QueryBuilder}. */ Q forCurrentThread() { // Process.myTid() seems to have issues on some devices (see Github #376) and Robolectric (#171): // We use currentThread().getId() instead (unfortunately return a long, can not use SparseArray). // PS.: thread ID may be reused, which should be fine because old thread will be gone anyway. long threadId = Thread.currentThread().getId(); synchronized (queriesForThreads) { WeakReference queryRef = queriesForThreads.get(threadId); Q query = queryRef != null ? queryRef.get() : null; if (query == null) { gc(); query = createQuery(); queriesForThreads.put(threadId, new WeakReference(query)); } else { System.arraycopy(initialValues, 0, query.parameters, 0, initialValues.length); } return query; } } abstract protected Q createQuery(); void gc() { synchronized (queriesForThreads) { Iterator>> iterator = queriesForThreads.entrySet().iterator(); while (iterator.hasNext()) { Entry> entry = iterator.next(); if (entry.getValue().get() == null) { iterator.remove(); } } } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/AbstractQueryWithLimit.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import org.greenrobot.greendao.AbstractDao; /** * Base class for queries returning data (entities or cursor). * * @param The entity class the query will return results for. * @author Markus */ // TODO Query for PKs/ROW IDs abstract class AbstractQueryWithLimit extends AbstractQuery { protected final int limitPosition; protected final int offsetPosition; protected AbstractQueryWithLimit(AbstractDao dao, String sql, String[] initialValues, int limitPosition, int offsetPosition) { super(dao, sql, initialValues); this.limitPosition = limitPosition; this.offsetPosition = offsetPosition; } /** * Sets the parameter (0 based) using the position in which it was added during building the query. Note: all * standard WHERE parameters come first. After that come the WHERE parameters of joins (if any). */ public AbstractQueryWithLimit setParameter(int index, Object parameter) { if (index >= 0 && (index == limitPosition || index == offsetPosition)) { throw new IllegalArgumentException("Illegal parameter index: " + index); } return (AbstractQueryWithLimit) super.setParameter(index, parameter); } /** * Sets the limit of the maximum number of results returned by this Query. {@link * org.greenrobot.greendao.query.QueryBuilder#limit(int)} must * have been called on the QueryBuilder that created this Query object. */ public void setLimit(int limit) { checkThread(); if (limitPosition == -1) { throw new IllegalStateException("Limit must be set with QueryBuilder before it can be used here"); } parameters[limitPosition] = Integer.toString(limit); } /** * Sets the offset for results returned by this Query. {@link org.greenrobot.greendao.query.QueryBuilder#offset(int)} must * have been called on * the QueryBuilder that created this Query object. */ public void setOffset(int offset) { checkThread(); if (offsetPosition == -1) { throw new IllegalStateException("Offset must be set with QueryBuilder before it can be used here"); } parameters[offsetPosition] = Integer.toString(offset); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/CloseableListIterator.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import java.io.Closeable; import java.util.ListIterator; /** * A list iterator that needs to be closed (or the associated list) to free underlying resources like a database cursor. * Typically used with LazyList. * * @author Markus * * @param */ public interface CloseableListIterator extends ListIterator, Closeable { } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/CountQuery.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import android.database.Cursor; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoException; import java.util.Date; public class CountQuery extends AbstractQuery { private final static class QueryData extends AbstractQueryData> { private QueryData(AbstractDao dao, String sql, String[] initialValues) { super(dao, sql, initialValues); } @Override protected CountQuery createQuery() { return new CountQuery(this, dao, sql, initialValues.clone()); } } static CountQuery create(AbstractDao dao, String sql, Object[] initialValues) { QueryData queryData = new QueryData(dao, sql, toStringArray(initialValues)); return queryData.forCurrentThread(); } private final QueryData queryData; private CountQuery(QueryData queryData, AbstractDao dao, String sql, String[] initialValues) { super(dao, sql, initialValues); this.queryData = queryData; } public CountQuery forCurrentThread() { return queryData.forCurrentThread(this); } /** Returns the count (number of results matching the query). Uses SELECT COUNT (*) sematics. */ public long count() { checkThread(); Cursor cursor = dao.getDatabase().rawQuery(sql, parameters); try { if (!cursor.moveToNext()) { throw new DaoException("No result for count"); } else if (!cursor.isLast()) { throw new DaoException("Unexpected row count: " + cursor.getCount()); } else if (cursor.getColumnCount() != 1) { throw new DaoException("Unexpected column count: " + cursor.getColumnCount()); } return cursor.getLong(0); } finally { cursor.close(); } } // copy setParameter methods to allow easy chaining @Override public CountQuery setParameter(int index, Object parameter) { return (CountQuery) super.setParameter(index, parameter); } @Override public CountQuery setParameter(int index, Date parameter) { return (CountQuery) super.setParameter(index, parameter); } @Override public CountQuery setParameter(int index, Boolean parameter) { return (CountQuery) super.setParameter(index, parameter); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/CursorQuery.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import android.database.Cursor; import org.greenrobot.greendao.AbstractDao; import java.util.Date; /** * A repeatable query returning a raw android.database.Cursor. Note, that using cursors is usually a hassle and * greenDAO provides a higher level abstraction using entities (see {@link org.greenrobot.greendao.query.Query}). This class * can nevertheless be useful to work with legacy code that is based on Cursors or CursorLoaders. * * @param The entity class the query will return results for. * @author Markus */ public class CursorQuery extends AbstractQueryWithLimit { private final static class QueryData extends AbstractQueryData> { private final int limitPosition; private final int offsetPosition; QueryData(AbstractDao dao, String sql, String[] initialValues, int limitPosition, int offsetPosition) { super(dao, sql, initialValues); this.limitPosition = limitPosition; this.offsetPosition = offsetPosition; } @Override protected CursorQuery createQuery() { return new CursorQuery(this, dao, sql, initialValues.clone(), limitPosition, offsetPosition); } } /** For internal use by greenDAO only. */ public static CursorQuery internalCreate(AbstractDao dao, String sql, Object[] initialValues) { return create(dao, sql, initialValues, -1, -1); } static CursorQuery create(AbstractDao dao, String sql, Object[] initialValues, int limitPosition, int offsetPosition) { QueryData queryData = new QueryData(dao, sql, toStringArray(initialValues), limitPosition, offsetPosition); return queryData.forCurrentThread(); } private final QueryData queryData; private CursorQuery(QueryData queryData, AbstractDao dao, String sql, String[] initialValues, int limitPosition, int offsetPosition) { super(dao, sql, initialValues, limitPosition, offsetPosition); this.queryData = queryData; } public CursorQuery forCurrentThread() { return queryData.forCurrentThread(this); } /** Executes the query and returns a raw android.database.Cursor. Don't forget to close it. */ public Cursor query() { checkThread(); return dao.getDatabase().rawQuery(sql, parameters); } // copy setParameter methods to allow easy chaining @Override public CursorQuery setParameter(int index, Object parameter) { return (CursorQuery) super.setParameter(index, parameter); } @Override public CursorQuery setParameter(int index, Date parameter) { return (CursorQuery) super.setParameter(index, parameter); } @Override public CursorQuery setParameter(int index, Boolean parameter) { return (CursorQuery) super.setParameter(index, parameter); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/DeleteQuery.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.database.Database; import java.util.Date; /** * A repeatable query for deleting entities.
* New API note: this is more likely to change. * * @param The entity class the query will delete from. * @author Markus */ public class DeleteQuery extends AbstractQuery { private final static class QueryData extends AbstractQueryData> { private QueryData(AbstractDao dao, String sql, String[] initialValues) { super(dao, sql, initialValues); } @Override protected DeleteQuery createQuery() { return new DeleteQuery(this, dao, sql, initialValues.clone()); } } static DeleteQuery create(AbstractDao dao, String sql, Object[] initialValues) { QueryData queryData = new QueryData(dao, sql, toStringArray(initialValues)); return queryData.forCurrentThread(); } private final QueryData queryData; private DeleteQuery(QueryData queryData, AbstractDao dao, String sql, String[] initialValues) { super(dao, sql, initialValues); this.queryData = queryData; } public DeleteQuery forCurrentThread() { return queryData.forCurrentThread(this); } /** * Deletes all matching entities without detaching them from the identity scope (aka session/cache). Note that this * method may lead to stale entity objects in the session cache. Stale entities may be returned when loaded by * their * primary key, but not using queries. */ public void executeDeleteWithoutDetachingEntities() { checkThread(); Database db = dao.getDatabase(); if (db.isDbLockedByCurrentThread()) { dao.getDatabase().execSQL(sql, parameters); } else { // Do TX to acquire a connection before locking this to avoid deadlocks // Locking order as described in AbstractDao db.beginTransaction(); try { dao.getDatabase().execSQL(sql, parameters); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } } // copy setParameter methods to allow easy chaining @Override public DeleteQuery setParameter(int index, Object parameter) { return (DeleteQuery) super.setParameter(index, parameter); } @Override public DeleteQuery setParameter(int index, Date parameter) { return (DeleteQuery) super.setParameter(index, parameter); } @Override public DeleteQuery setParameter(int index, Boolean parameter) { return (DeleteQuery) super.setParameter(index, parameter); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/Join.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.Property; /** * A Join lets you relate to other entity types for queries, and allows using WHERE statements on the joined entity * type. */ public class Join { final String sourceTablePrefix; final AbstractDao daoDestination; final Property joinPropertySource; final Property joinPropertyDestination; final String tablePrefix; final WhereCollector whereCollector; public Join(String sourceTablePrefix, Property sourceJoinProperty, AbstractDao daoDestination, Property destinationJoinProperty, String joinTablePrefix) { this.sourceTablePrefix = sourceTablePrefix; this.joinPropertySource = sourceJoinProperty; this.daoDestination = daoDestination; this.joinPropertyDestination = destinationJoinProperty; tablePrefix = joinTablePrefix; whereCollector = new WhereCollector(daoDestination, joinTablePrefix); } /** * Adds the given conditions to the where clause using an logical AND. To create new conditions, use the properties * given in the generated dao classes. */ public Join where(WhereCondition cond, WhereCondition... condMore) { whereCollector.add(cond, condMore); return this; } /** * Adds the given conditions to the where clause using an logical OR. To create new conditions, use the properties * given in the generated dao classes. */ public Join whereOr(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) { whereCollector.add(or(cond1, cond2, condMore)); return this; } /** * Creates a WhereCondition by combining the given conditions using OR. The returned WhereCondition must be used * inside {@link #where(WhereCondition, WhereCondition...)} or * {@link #whereOr(WhereCondition, WhereCondition, WhereCondition...)}. */ public WhereCondition or(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) { return whereCollector.combineWhereConditions(" OR ", cond1, cond2, condMore); } /** * Creates a WhereCondition by combining the given conditions using AND. The returned WhereCondition must be used * inside {@link #where(WhereCondition, WhereCondition...)} or * {@link #whereOr(WhereCondition, WhereCondition, WhereCondition...)}. */ public WhereCondition and(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) { return whereCollector.combineWhereConditions(" AND ", cond1, cond2, condMore); } /** * Usually you don't need this value; just in case you are mixing custom * {@link org.greenrobot.greendao.query.WhereCondition.StringCondition} into the query, this value allows to reference * the joined (target) table. */ public String getTablePrefix() { return tablePrefix; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/LazyList.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import java.io.Closeable; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.concurrent.locks.ReentrantLock; import android.database.Cursor; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.InternalQueryDaoAccess; /** * A thread-safe, unmodifiable list that reads entities once they are accessed from an underlying database cursor. Make * sure to close the list once you are done with it. The lazy list can be cached or not. Cached lazy lists store the * entities in memory to avoid loading entities more than once. Some features of the list are limited to cached lists * (e.g. features that require the entire list). Cached lists close the cursor automatically once you queried all * entities. However, to avoid leaked cursors, you should not rely on this behavior: if an exception occurs before the * entire list is read, you should close the lazy list (and thus the underlying cursor) on your own to be on the safe * side. * * @author Markus * * @param * Entity type. */ public class LazyList implements List, Closeable { protected class LazyIterator implements CloseableListIterator { private int index; private final boolean closeWhenDone; public LazyIterator(int startLocation, boolean closeWhenDone) { index = startLocation; this.closeWhenDone = closeWhenDone; } @Override public void add(E object) { throw new UnsupportedOperationException(); } @Override /** FIXME: before hasPrevious(), next() must be called. */ public boolean hasPrevious() { return index > 0; } @Override public int nextIndex() { return index; } @Override /** FIXME: before previous(), next() must be called. */ public E previous() { if (index <= 0) { throw new NoSuchElementException(); } index--; E entity = get(index); // if (index == size && closeWhenDone) { // close(); // } return entity; } @Override public int previousIndex() { return index - 1; } @Override public void set(E object) { throw new UnsupportedOperationException(); } @Override public boolean hasNext() { return index < size; } @Override public E next() { if (index >= size) { throw new NoSuchElementException(); } E entity = get(index); index++; if (index == size && closeWhenDone) { close(); } return entity; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void close() { LazyList.this.close(); } } private final InternalQueryDaoAccess daoAccess; private final Cursor cursor; private final List entities; private final int size; private final ReentrantLock lock; private volatile int loadedCount; LazyList(InternalQueryDaoAccess daoAccess, Cursor cursor, boolean cacheEntities) { this.cursor = cursor; this.daoAccess = daoAccess; size = cursor.getCount(); if (cacheEntities) { entities = new ArrayList(size); for (int i = 0; i < size; i++) { entities.add(null); } } else { entities = null; } if (size == 0) { cursor.close(); } lock = new ReentrantLock(); } /** Loads the remaining entities (if any) that were not loaded before. Applies to cached lazy lists only. */ public void loadRemaining() { checkCached(); int size = entities.size(); for (int i = 0; i < size; i++) { get(i); } } protected void checkCached() { if (entities == null) { throw new DaoException("This operation only works with cached lazy lists"); } } /** Like get but does not load the entity if it was not loaded before. */ public E peek(int location) { if (entities != null) { return entities.get(location); } else { return null; } } @Override /** Closes the underlying cursor: do not try to get entities not loaded (using get) before. */ public void close() { cursor.close(); } public boolean isClosed() { return cursor.isClosed(); } public int getLoadedCount() { return loadedCount; } public boolean isLoadedCompletely() { return loadedCount == size; } @Override public boolean add(E object) { throw new UnsupportedOperationException(); } @Override public void add(int location, E object) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection arg0) { throw new UnsupportedOperationException(); } @Override public boolean addAll(int arg0, Collection arg1) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public boolean contains(Object object) { loadRemaining(); return entities.contains(object); } @Override public boolean containsAll(Collection collection) { loadRemaining(); return entities.containsAll(collection); } @Override public E get(int location) { if (entities != null) { E entity = entities.get(location); if (entity == null) { lock.lock(); try { entity = entities.get(location); if (entity == null) { entity = loadEntity(location); entities.set(location, entity); // Ignore FindBugs: increment of volatile is fine here because we use a lock loadedCount++; if (loadedCount == size) { cursor.close(); } } } finally { lock.unlock(); } } return entity; } else { lock.lock(); try { return loadEntity(location); } finally { lock.unlock(); } } } /** Lock must be locked when entering this method. */ protected E loadEntity(int location) { boolean ok = cursor.moveToPosition(location); if(!ok) { throw new DaoException("Could not move to cursor location " + location); } E entity = daoAccess.loadCurrent(cursor, 0, true); if (entity == null) { throw new DaoException("Loading of entity failed (null) at position " + location); } return entity; } @Override public int indexOf(Object object) { loadRemaining(); return entities.indexOf(object); } @Override public boolean isEmpty() { return size == 0; } @Override public Iterator iterator() { return new LazyIterator(0, false); } @Override public int lastIndexOf(Object object) { loadRemaining(); return entities.lastIndexOf(object); } @Override public CloseableListIterator listIterator() { return new LazyIterator(0, false); } /** Closes this list's cursor once the iterator is fully iterated through. */ public CloseableListIterator listIteratorAutoClose() { return new LazyIterator(0, true); } @Override public ListIterator listIterator(int location) { return new LazyIterator(location, false); } @Override public E remove(int location) { throw new UnsupportedOperationException(); } @Override public boolean remove(Object object) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection arg0) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection arg0) { throw new UnsupportedOperationException(); } @Override public E set(int location, E object) { throw new UnsupportedOperationException(); } @Override public int size() { return size; } @Override public List subList(int start, int end) { checkCached(); for (int i = start; i < end; i++) { get(i); } return entities.subList(start, end); } @Override public Object[] toArray() { loadRemaining(); return entities.toArray(); } @Override public T[] toArray(T[] array) { loadRemaining(); return entities.toArray(array); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/Query.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import android.database.Cursor; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.annotation.apihint.Internal; import org.greenrobot.greendao.rx.RxQuery; import org.greenrobot.greendao.rx.RxTransaction; import java.util.Date; import java.util.List; import rx.schedulers.Schedulers; /** * A repeatable query returning entities. * * @param The entity class the query will return results for. * @author Markus */ public class Query extends AbstractQueryWithLimit { private final static class QueryData extends AbstractQueryData> { private final int limitPosition; private final int offsetPosition; QueryData(AbstractDao dao, String sql, String[] initialValues, int limitPosition, int offsetPosition) { super(dao, sql, initialValues); this.limitPosition = limitPosition; this.offsetPosition = offsetPosition; } @Override protected Query createQuery() { return new Query(this, dao, sql, initialValues.clone(), limitPosition, offsetPosition); } } /** For internal use by greenDAO only. */ public static Query internalCreate(AbstractDao dao, String sql, Object[] initialValues) { return create(dao, sql, initialValues, -1, -1); } static Query create(AbstractDao dao, String sql, Object[] initialValues, int limitPosition, int offsetPosition) { QueryData queryData = new QueryData(dao, sql, toStringArray(initialValues), limitPosition, offsetPosition); return queryData.forCurrentThread(); } private final QueryData queryData; private volatile RxQuery rxTxPlain; private volatile RxQuery rxTxIo; private Query(QueryData queryData, AbstractDao dao, String sql, String[] initialValues, int limitPosition, int offsetPosition) { super(dao, sql, initialValues, limitPosition, offsetPosition); this.queryData = queryData; } /** * Note: all parameters are reset to their initial values specified in {@link QueryBuilder}. */ public Query forCurrentThread() { return queryData.forCurrentThread(this); } /** Executes the query and returns the result as a list containing all entities loaded into memory. */ public List list() { checkThread(); Cursor cursor = dao.getDatabase().rawQuery(sql, parameters); return daoAccess.loadAllAndCloseCursor(cursor); } /** * Executes the query and returns the result as a list that lazy loads the entities on first access. Entities are * cached, so accessing the same entity more than once will not result in loading an entity from the underlying * cursor again.Make sure to close it to close the underlying cursor. */ public LazyList listLazy() { checkThread(); Cursor cursor = dao.getDatabase().rawQuery(sql, parameters); return new LazyList(daoAccess, cursor, true); } /** * Executes the query and returns the result as a list that lazy loads the entities on every access (uncached). * Make sure to close the list to close the underlying cursor. */ public LazyList listLazyUncached() { checkThread(); Cursor cursor = dao.getDatabase().rawQuery(sql, parameters); return new LazyList(daoAccess, cursor, false); } /** * Executes the query and returns the result as a list iterator; make sure to close it to close the underlying * cursor. The cursor is closed once the iterator is fully iterated through. */ public CloseableListIterator listIterator() { return listLazyUncached().listIteratorAutoClose(); } /** * Executes the query and returns the unique result or null. * * @return Entity or null if no matching entity was found * @throws DaoException if the result is not unique */ public T unique() { checkThread(); Cursor cursor = dao.getDatabase().rawQuery(sql, parameters); return daoAccess.loadUniqueAndCloseCursor(cursor); } /** * Executes the query and returns the unique result (never null). * * @return Entity * @throws DaoException if the result is not unique or no entity was found */ public T uniqueOrThrow() { T entity = unique(); if (entity == null) { throw new DaoException("No entity found for query"); } return entity; } @Override public Query setParameter(int index, Object parameter) { return (Query) super.setParameter(index, parameter); } @Override public Query setParameter(int index, Date parameter) { return (Query) super.setParameter(index, parameter); } @Override public Query setParameter(int index, Boolean parameter) { return (Query) super.setParameter(index, parameter); } /** * DO NOT USE. * The returned {@link RxTransaction} allows getting query results using Rx Observables without any Scheduler set * for subscribeOn. * * @see #__InternalRx() */ @Internal public RxQuery __internalRxPlain() { if (rxTxPlain == null) { rxTxPlain = new RxQuery(this); } return rxTxPlain; } /** * DO NOT USE. * The returned {@link RxTransaction} allows getting query results using Rx Observables using RX's IO scheduler for * subscribeOn. * * @see #__internalRxPlain() */ @Internal public RxQuery __InternalRx() { if (rxTxIo == null) { rxTxIo = new RxQuery(this, Schedulers.io()); } return rxTxIo; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/QueryBuilder.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import android.database.sqlite.SQLiteDatabase; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.AbstractDaoSession; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.DaoLog; import org.greenrobot.greendao.Property; import org.greenrobot.greendao.annotation.apihint.Experimental; import org.greenrobot.greendao.internal.SqlUtils; import org.greenrobot.greendao.rx.RxQuery; import java.util.ArrayList; import java.util.List; /** * Builds custom entity queries using constraints and parameters and without SQL (QueryBuilder creates SQL for you). To * acquire an QueryBuilder, use {@link AbstractDao#queryBuilder()} or {@link AbstractDaoSession#queryBuilder(Class)}. * Entity properties are referenced by Fields in the "Properties" inner class of the generated DAOs. This approach * allows compile time checks and prevents typo errors occuring at build time.
*
* Example: Query for all users with the first name "Joe" ordered by their last name. (The class Properties is an inner * class of UserDao and should be imported before.)
* * List joes = dao.queryBuilder().where(Properties.FirstName.eq("Joe")).orderAsc(Properties.LastName).list(); * * * @param Entity class to create an query for. * @author Markus */ public class QueryBuilder { /** Set to true to debug the SQL. */ public static boolean LOG_SQL; /** Set to see the given values. */ public static boolean LOG_VALUES; private final WhereCollector whereCollector; private StringBuilder orderBuilder; private final List values; private final List> joins; private final AbstractDao dao; private final String tablePrefix; private Integer limit; private Integer offset; private boolean distinct; /** stored with a leading space */ private String stringOrderCollation; /** For internal use by greenDAO only. */ public static QueryBuilder internalCreate(AbstractDao dao) { return new QueryBuilder(dao); } protected QueryBuilder(AbstractDao dao) { this(dao, "T"); } protected QueryBuilder(AbstractDao dao, String tablePrefix) { this.dao = dao; this.tablePrefix = tablePrefix; values = new ArrayList(); joins = new ArrayList>(); whereCollector = new WhereCollector(dao, tablePrefix); stringOrderCollation = " COLLATE NOCASE"; } private void checkOrderBuilder() { if (orderBuilder == null) { orderBuilder = new StringBuilder(); } else if (orderBuilder.length() > 0) { orderBuilder.append(","); } } /** Use a SELECT DISTINCT to avoid duplicate entities returned, e.g. when doing joins. */ public QueryBuilder distinct() { distinct = true; return this; } /** * If using Android's embedded SQLite, this enables localized ordering of strings * (see {@link #orderAsc(Property...)} and {@link #orderDesc(Property...)}). This uses "COLLATE LOCALIZED", which * is unavailable in SQLCipher (in that case, the ordering is unchanged). * * @see #stringOrderCollation */ public QueryBuilder preferLocalizedStringOrder() { // SQLCipher 3.5.0+ does not understand "COLLATE LOCALIZED" if (dao.getDatabase().getRawDatabase() instanceof SQLiteDatabase) { stringOrderCollation = " COLLATE LOCALIZED"; } return this; } /** * Customizes the ordering of strings used by {@link #orderAsc(Property...)} and {@link #orderDesc(Property...)}. * Default is "COLLATE NOCASE". * * @see #preferLocalizedStringOrder */ public QueryBuilder stringOrderCollation(String stringOrderCollation) { this.stringOrderCollation = stringOrderCollation == null || stringOrderCollation.startsWith(" ") ? stringOrderCollation : " " + stringOrderCollation; return this; } /** * Adds the given conditions to the where clause using an logical AND. To create new conditions, use the properties * given in the generated dao classes. */ public QueryBuilder where(WhereCondition cond, WhereCondition... condMore) { whereCollector.add(cond, condMore); return this; } /** * Adds the given conditions to the where clause using an logical OR. To create new conditions, use the properties * given in the generated dao classes. */ public QueryBuilder whereOr(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) { whereCollector.add(or(cond1, cond2, condMore)); return this; } /** * Creates a WhereCondition by combining the given conditions using OR. The returned WhereCondition must be used * inside {@link #where(WhereCondition, WhereCondition...)} or * {@link #whereOr(WhereCondition, WhereCondition, WhereCondition...)}. */ public WhereCondition or(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) { return whereCollector.combineWhereConditions(" OR ", cond1, cond2, condMore); } /** * Creates a WhereCondition by combining the given conditions using AND. The returned WhereCondition must be used * inside {@link #where(WhereCondition, WhereCondition...)} or * {@link #whereOr(WhereCondition, WhereCondition, WhereCondition...)}. */ public WhereCondition and(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) { return whereCollector.combineWhereConditions(" AND ", cond1, cond2, condMore); } /** * Expands the query to another entity type by using a JOIN. The primary key property of the primary entity for * this QueryBuilder is used to match the given destinationProperty. */ public Join join(Class destinationEntityClass, Property destinationProperty) { return join(dao.getPkProperty(), destinationEntityClass, destinationProperty); } /** * Expands the query to another entity type by using a JOIN. The given sourceProperty is used to match the primary * key property of the given destinationEntity. */ public Join join(Property sourceProperty, Class destinationEntityClass) { AbstractDao destinationDao = (AbstractDao) dao.getSession().getDao(destinationEntityClass); Property destinationProperty = destinationDao.getPkProperty(); return addJoin(tablePrefix, sourceProperty, destinationDao, destinationProperty); } /** * Expands the query to another entity type by using a JOIN. The given sourceProperty is used to match the given * destinationProperty of the given destinationEntity. */ public Join join(Property sourceProperty, Class destinationEntityClass, Property destinationProperty) { AbstractDao destinationDao = (AbstractDao) dao.getSession().getDao(destinationEntityClass); return addJoin(tablePrefix, sourceProperty, destinationDao, destinationProperty); } /** * Expands the query to another entity type by using a JOIN. The given sourceJoin's property is used to match the * given destinationProperty of the given destinationEntity. Note that destination entity of the given join is used * as the source for the new join to add. In this way, it is possible to compose complex "join of joins" across * several entities if required. */ public Join join(Join sourceJoin, Property sourceProperty, Class destinationEntityClass, Property destinationProperty) { AbstractDao destinationDao = (AbstractDao) dao.getSession().getDao(destinationEntityClass); return addJoin(sourceJoin.tablePrefix, sourceProperty, destinationDao, destinationProperty); } private Join addJoin(String sourceTablePrefix, Property sourceProperty, AbstractDao destinationDao, Property destinationProperty) { String joinTablePrefix = "J" + (joins.size() + 1); Join join = new Join(sourceTablePrefix, sourceProperty, destinationDao, destinationProperty, joinTablePrefix); joins.add(join); return join; } /** Adds the given properties to the ORDER BY section using ascending order. */ public QueryBuilder orderAsc(Property... properties) { orderAscOrDesc(" ASC", properties); return this; } /** Adds the given properties to the ORDER BY section using descending order. */ public QueryBuilder orderDesc(Property... properties) { orderAscOrDesc(" DESC", properties); return this; } private void orderAscOrDesc(String ascOrDescWithLeadingSpace, Property... properties) { for (Property property : properties) { checkOrderBuilder(); append(orderBuilder, property); if (String.class.equals(property.type) && stringOrderCollation != null) { orderBuilder.append(stringOrderCollation); } orderBuilder.append(ascOrDescWithLeadingSpace); } } /** Adds the given properties to the ORDER BY section using the given custom order. */ public QueryBuilder orderCustom(Property property, String customOrderForProperty) { checkOrderBuilder(); append(orderBuilder, property).append(' '); orderBuilder.append(customOrderForProperty); return this; } /** * Adds the given raw SQL string to the ORDER BY section. Do not use this for standard properties: orderAsc and * orderDesc are preferred. */ public QueryBuilder orderRaw(String rawOrder) { checkOrderBuilder(); orderBuilder.append(rawOrder); return this; } protected StringBuilder append(StringBuilder builder, Property property) { whereCollector.checkProperty(property); builder.append(tablePrefix).append('.').append('\'').append(property.columnName).append('\''); return builder; } /** Limits the number of results returned by queries. */ public QueryBuilder limit(int limit) { this.limit = limit; return this; } /** * Sets the offset for query results in combination with {@link #limit(int)}. The first {@code offset} results are * skipped and the total number of results will be limited by {@code limit}. You cannot use offset without limit. */ public QueryBuilder offset(int offset) { this.offset = offset; return this; } /** * Builds a reusable query object (Query objects can be executed more efficiently than creating a QueryBuilder for * each execution. */ public Query build() { StringBuilder builder = createSelectBuilder(); int limitPosition = checkAddLimit(builder); int offsetPosition = checkAddOffset(builder); String sql = builder.toString(); checkLog(sql); return Query.create(dao, sql, values.toArray(), limitPosition, offsetPosition); } /** * Builds a reusable query object for low level android.database.Cursor access. * (Query objects can be executed more efficiently than creating a QueryBuilder for each execution. */ public CursorQuery buildCursor() { StringBuilder builder = createSelectBuilder(); int limitPosition = checkAddLimit(builder); int offsetPosition = checkAddOffset(builder); String sql = builder.toString(); checkLog(sql); return CursorQuery.create(dao, sql, values.toArray(), limitPosition, offsetPosition); } private StringBuilder createSelectBuilder() { String select = SqlUtils.createSqlSelect(dao.getTablename(), tablePrefix, dao.getAllColumns(), distinct); StringBuilder builder = new StringBuilder(select); appendJoinsAndWheres(builder, tablePrefix); if (orderBuilder != null && orderBuilder.length() > 0) { builder.append(" ORDER BY ").append(orderBuilder); } return builder; } private int checkAddLimit(StringBuilder builder) { int limitPosition = -1; if (limit != null) { builder.append(" LIMIT ?"); values.add(limit); limitPosition = values.size() - 1; } return limitPosition; } private int checkAddOffset(StringBuilder builder) { int offsetPosition = -1; if (offset != null) { if (limit == null) { throw new IllegalStateException("Offset cannot be set without limit"); } builder.append(" OFFSET ?"); values.add(offset); offsetPosition = values.size() - 1; } return offsetPosition; } /** * Builds a reusable query object for deletion (Query objects can be executed more efficiently than creating a * QueryBuilder for each execution. */ public DeleteQuery buildDelete() { if (!joins.isEmpty()) { throw new DaoException("JOINs are not supported for DELETE queries"); } String tablename = dao.getTablename(); String baseSql = SqlUtils.createSqlDelete(tablename, null); StringBuilder builder = new StringBuilder(baseSql); // tablePrefix gets replaced by table name below. Don't use tableName here because it causes trouble when // table name ends with tablePrefix. appendJoinsAndWheres(builder, tablePrefix); String sql = builder.toString(); // Remove table aliases, not supported for DELETE queries. // TODO(?): don't create table aliases in the first place. sql = sql.replace(tablePrefix + ".\"", '"' + tablename + "\".\""); checkLog(sql); return DeleteQuery.create(dao, sql, values.toArray()); } /** * Builds a reusable query object for counting rows (Query objects can be executed more efficiently than creating a * QueryBuilder for each execution. */ public CountQuery buildCount() { String tablename = dao.getTablename(); String baseSql = SqlUtils.createSqlSelectCountStar(tablename, tablePrefix); StringBuilder builder = new StringBuilder(baseSql); appendJoinsAndWheres(builder, tablePrefix); String sql = builder.toString(); checkLog(sql); return CountQuery.create(dao, sql, values.toArray()); } private void checkLog(String sql) { if (LOG_SQL) { DaoLog.d("Built SQL for query: " + sql); } if (LOG_VALUES) { DaoLog.d("Values for query: " + values); } } private void appendJoinsAndWheres(StringBuilder builder, String tablePrefixOrNull) { values.clear(); for (Join join : joins) { builder.append(" JOIN "); builder.append('"').append(join.daoDestination.getTablename()).append('"').append(' '); builder.append(join.tablePrefix).append(" ON "); SqlUtils.appendProperty(builder, join.sourceTablePrefix, join.joinPropertySource).append('='); SqlUtils.appendProperty(builder, join.tablePrefix, join.joinPropertyDestination); } boolean whereAppended = !whereCollector.isEmpty(); if (whereAppended) { builder.append(" WHERE "); whereCollector.appendWhereClause(builder, tablePrefixOrNull, values); } for (Join join : joins) { if (!join.whereCollector.isEmpty()) { if (!whereAppended) { builder.append(" WHERE "); whereAppended = true; } else { builder.append(" AND "); } join.whereCollector.appendWhereClause(builder, join.tablePrefix, values); } } } /** * Shorthand for {@link QueryBuilder#build() build()}.{@link Query#list() list()}; see {@link Query#list()} for * details. To execute a query more than once, you should build the query and keep the {@link Query} object for * efficiency reasons. */ public List list() { return build().list(); } /** * Shorthand for {@link QueryBuilder#build() build()}.{@link Query#__InternalRx()}. */ @Experimental public RxQuery rx() { return build().__InternalRx(); } /** * Shorthand for {@link QueryBuilder#build() build()}.{@link Query#__internalRxPlain()}. */ @Experimental public RxQuery rxPlain() { return build().__internalRxPlain(); } /** * Shorthand for {@link QueryBuilder#build() build()}.{@link Query#listLazy() listLazy()}; see * {@link Query#listLazy()} for details. To execute a query more than once, you should build the query and keep the * {@link Query} object for efficiency reasons. */ public LazyList listLazy() { return build().listLazy(); } /** * Shorthand for {@link QueryBuilder#build() build()}.{@link Query#listLazyUncached() listLazyUncached()}; see * {@link Query#listLazyUncached()} for details. To execute a query more than once, you should build the query and * keep the {@link Query} object for efficiency reasons. */ public LazyList listLazyUncached() { return build().listLazyUncached(); } /** * Shorthand for {@link QueryBuilder#build() build()}.{@link Query#listIterator() listIterator()}; see * {@link Query#listIterator()} for details. To execute a query more than once, you should build the query and keep * the {@link Query} object for efficiency reasons. */ public CloseableListIterator listIterator() { return build().listIterator(); } /** * Shorthand for {@link QueryBuilder#build() build()}.{@link Query#unique() unique()}; see {@link Query#unique()} * for details. To execute a query more than once, you should build the query and keep the {@link Query} object for * efficiency reasons. */ public T unique() { return build().unique(); } /** * Shorthand for {@link QueryBuilder#build() build()}.{@link Query#uniqueOrThrow() uniqueOrThrow()}; see * {@link Query#uniqueOrThrow()} for details. To execute a query more than once, you should build the query and * keep * the {@link Query} object for efficiency reasons. */ public T uniqueOrThrow() { return build().uniqueOrThrow(); } /** * Shorthand for {@link QueryBuilder#buildCount() buildCount()}.{@link CountQuery#count() count()}; see * {@link CountQuery#count()} for details. To execute a query more than once, you should build the query and keep * the {@link CountQuery} object for efficiency reasons. */ public long count() { return buildCount().count(); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/WhereCollector.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.Property; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; /** Internal class to collect WHERE conditions. */ class WhereCollector { private final AbstractDao dao; private final List whereConditions; private final String tablePrefix; WhereCollector(AbstractDao dao, String tablePrefix) { this.dao = dao; this.tablePrefix = tablePrefix; whereConditions = new ArrayList(); } void add(WhereCondition cond, WhereCondition... condMore) { checkCondition(cond); whereConditions.add(cond); for (WhereCondition whereCondition : condMore) { checkCondition(whereCondition); whereConditions.add(whereCondition); } } WhereCondition combineWhereConditions(String combineOp, WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) { StringBuilder builder = new StringBuilder("("); List combinedValues = new ArrayList(); addCondition(builder, combinedValues, cond1); builder.append(combineOp); addCondition(builder, combinedValues, cond2); for (WhereCondition cond : condMore) { builder.append(combineOp); addCondition(builder, combinedValues, cond); } builder.append(')'); return new WhereCondition.StringCondition(builder.toString(), combinedValues.toArray()); } void addCondition(StringBuilder builder, List values, WhereCondition condition) { checkCondition(condition); condition.appendTo(builder, tablePrefix); condition.appendValuesTo(values); } void checkCondition(WhereCondition whereCondition) { if (whereCondition instanceof WhereCondition.PropertyCondition) { checkProperty(((WhereCondition.PropertyCondition) whereCondition).property); } } void checkProperty(Property property) { if (dao != null) { Property[] properties = dao.getProperties(); boolean found = false; for (Property property2 : properties) { if (property == property2) { found = true; break; } } if (!found) { throw new DaoException("Property '" + property.name + "' is not part of " + dao); } } } void appendWhereClause(StringBuilder builder, String tablePrefixOrNull, List values) { ListIterator iter = whereConditions.listIterator(); while (iter.hasNext()) { if (iter.hasPrevious()) { builder.append(" AND "); } WhereCondition condition = iter.next(); condition.appendTo(builder, tablePrefixOrNull); condition.appendValuesTo(values); } } boolean isEmpty() { return whereConditions.isEmpty(); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/query/WhereCondition.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.query; import java.util.Date; import java.util.List; import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.Property; import org.greenrobot.greendao.internal.SqlUtils; /** * Internal interface to model WHERE conditions used in queries. Use the {@link Property} objects in the DAO classes to * create new conditions. */ public interface WhereCondition { void appendTo(StringBuilder builder, String tableAlias); void appendValuesTo(List values); abstract class AbstractCondition implements WhereCondition { protected final boolean hasSingleValue; protected final Object value; protected final Object[] values; public AbstractCondition() { hasSingleValue = false; value = null; values = null; } public AbstractCondition(Object value) { this.value = value; hasSingleValue = true; values = null; } public AbstractCondition(Object[] values) { this.value = null; hasSingleValue = false; this.values = values; } @Override public void appendValuesTo(List valuesTarget) { if (hasSingleValue) { valuesTarget.add(value); } else if (values != null) { for (Object value : values) { valuesTarget.add(value); } } } } class PropertyCondition extends AbstractCondition { private static Object checkValueForType(Property property, Object value) { if (value != null && value.getClass().isArray()) { throw new DaoException("Illegal value: found array, but simple object required"); } Class type = property.type; if (type == Date.class) { if (value instanceof Date) { return ((Date) value).getTime(); } else if (value instanceof Long) { return value; } else { throw new DaoException("Illegal date value: expected java.util.Date or Long for value " + value); } } else if (property.type == boolean.class || property.type == Boolean.class) { if (value instanceof Boolean) { return ((Boolean) value) ? 1 : 0; } else if (value instanceof Number) { int intValue = ((Number) value).intValue(); if (intValue != 0 && intValue != 1) { throw new DaoException("Illegal boolean value: numbers must be 0 or 1, but was " + value); } } else if (value instanceof String) { String stringValue = ((String) value); if ("TRUE".equalsIgnoreCase(stringValue)) { return 1; } else if ("FALSE".equalsIgnoreCase(stringValue)) { return 0; } else { throw new DaoException( "Illegal boolean value: Strings must be \"TRUE\" or \"FALSE\" (case insensitive), but was " + value); } } } return value; } private static Object[] checkValuesForType(Property property, Object[] values) { for (int i = 0; i < values.length; i++) { values[i] = checkValueForType(property, values[i]); } return values; } public final Property property; public final String op; public PropertyCondition(Property property, String op) { this.property = property; this.op = op; } public PropertyCondition(Property property, String op, Object value) { super(checkValueForType(property, value)); this.property = property; this.op = op; } public PropertyCondition(Property property, String op, Object[] values) { super(checkValuesForType(property, values)); this.property = property; this.op = op; } @Override public void appendTo(StringBuilder builder, String tableAlias) { SqlUtils.appendProperty(builder, tableAlias, property).append(op); } } class StringCondition extends AbstractCondition { protected final String string; public StringCondition(String string) { this.string = string; } public StringCondition(String string, Object value) { super(value); this.string = string; } public StringCondition(String string, Object... values) { super(values); this.string = string; } @Override public void appendTo(StringBuilder builder, String tableAlias) { builder.append(string); } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/rx/RxBase.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.rx; import org.greenrobot.greendao.annotation.apihint.Experimental; import org.greenrobot.greendao.annotation.apihint.Internal; import java.util.concurrent.Callable; import rx.Observable; import rx.Scheduler; /** * Base functionality for Rx, e.g. default scheduler. */ @Internal class RxBase { protected final Scheduler scheduler; /** * No default scheduler. */ RxBase() { scheduler = null; } /** * Sets the default scheduler, which is used to configure returned observables with * {@link Observable#subscribeOn(Scheduler)}. */ @Experimental RxBase(Scheduler scheduler) { this.scheduler = scheduler; } /** * The default scheduler (or null) used for wrapping. */ @Experimental public Scheduler getScheduler() { return scheduler; } protected Observable wrap(Callable callable) { return wrap(RxUtils.fromCallable(callable)); } protected Observable wrap(Observable observable) { if (scheduler != null) { return observable.subscribeOn(scheduler); } else { return observable; } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/rx/RxDao.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.rx; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.annotation.apihint.Experimental; import java.util.List; import java.util.concurrent.Callable; import rx.Observable; import rx.Scheduler; /** * Like {@link AbstractDao} but with Rx support. Most methods from AbstractDao are present here, but will return an * {@link Observable}. Modifying operations return the given entities, so they can be further processed in Rx. *

* Instances of RxDao may have an default {@link rx.Scheduler}, which is used to configure returned observables with * {@link Observable#subscribeOn(Scheduler)} (see {@link AbstractDao#rxPlain()}, which uses the IO scheduler). * * Note: DO NOT call more than one data modification operation when you can use a transaction instead (see * {@link RxTransaction}. Individual calls use a transaction each and are much slower. * * @param Entity type * @param Primary key (PK) type; use Void if entity does not have exactly one PK * @see AbstractDao#rxPlain() */ @Experimental public class RxDao extends RxBase { private final AbstractDao dao; /** * Creates a new RxDao without a default scheduler. */ @Experimental public RxDao(AbstractDao dao) { this(dao, null); } /** * Creates a new RxDao with a default scheduler, which is used to configure returned observables with * {@link Observable#subscribeOn(Scheduler)}. */ @Experimental public RxDao(AbstractDao dao, Scheduler scheduler) { super(scheduler); this.dao = dao; } /** * Rx version of {@link AbstractDao#loadAll()} returning an Observable. */ @Experimental public Observable> loadAll() { return wrap(new Callable>() { @Override public List call() throws Exception { return dao.loadAll(); } }); } /** * Rx version of {@link AbstractDao#loadAll()} returning an Observable. */ @Experimental public Observable load(final K key) { return wrap(new Callable() { @Override public T call() throws Exception { return dao.load(key); } }); } /** * Rx version of {@link AbstractDao#refresh(Object)} returning an Observable. * Note that the Observable will emit the given entity back to its subscribers. */ @Experimental public Observable refresh(final T entity) { return wrap(new Callable() { @Override public T call() throws Exception { dao.refresh(entity); return entity; } }); } /** * Rx version of {@link AbstractDao#insert(Object)} returning an Observable. * Note that the Observable will emit the given entity back to its subscribers. */ @Experimental public Observable insert(final T entity) { return wrap(new Callable() { @Override public T call() throws Exception { dao.insert(entity); return entity; } }); } /** * Rx version of {@link AbstractDao#insertInTx(Iterable)} returning an Observable. * Note that the Observable will emit the given entities back to its subscribers. */ @Experimental public Observable> insertInTx(final Iterable entities) { return wrap(new Callable>() { @Override public Iterable call() throws Exception { dao.insertInTx(entities); return entities; } }); } /** * Rx version of {@link AbstractDao#insertInTx(Object[])} returning an Observable. * Note that the Observable will emit the given entities back to its subscribers. */ @Experimental public Observable insertInTx(final T... entities) { return wrap(new Callable() { @Override public Object[] call() throws Exception { dao.insertInTx(entities); return entities; } }); } /** * Rx version of {@link AbstractDao#insertOrReplace(Object)} returning an Observable. * Note that the Observable will emit the given entity back to its subscribers. */ @Experimental public Observable insertOrReplace(final T entity) { return wrap(new Callable() { @Override public T call() throws Exception { dao.insertOrReplace(entity); return entity; } }); } /** * Rx version of {@link AbstractDao#insertOrReplaceInTx(Iterable)} returning an Observable. * Note that the Observable will emit the given entities back to its subscribers. */ @Experimental public Observable> insertOrReplaceInTx(final Iterable entities) { return wrap(new Callable>() { @Override public Iterable call() throws Exception { dao.insertOrReplaceInTx(entities); return entities; } }); } /** * Rx version of {@link AbstractDao#insertOrReplaceInTx(Object[])} returning an Observable. * Note that the Observable will emit the given entities back to its subscribers. */ @Experimental public Observable insertOrReplaceInTx(final T... entities) { return wrap(new Callable() { @Override public Object[] call() throws Exception { dao.insertOrReplaceInTx(entities); return entities; } }); } /** * Rx version of {@link AbstractDao#save(Object)} returning an Observable. * Note that the Observable will emit the given entity back to its subscribers. */ @Experimental public Observable save(final T entity) { return wrap(new Callable() { @Override public T call() throws Exception { dao.save(entity); return entity; } }); } /** * Rx version of {@link AbstractDao#saveInTx(Iterable)} returning an Observable. * Note that the Observable will emit the given entities back to its subscribers. */ @Experimental public Observable> saveInTx(final Iterable entities) { return wrap(new Callable>() { @Override public Iterable call() throws Exception { dao.saveInTx(entities); return entities; } }); } /** * Rx version of {@link AbstractDao#saveInTx(Object[])} returning an Observable. * Note that the Observable will emit the given entities back to its subscribers. */ @Experimental public Observable saveInTx(final T... entities) { return wrap(new Callable() { @Override public Object[] call() throws Exception { dao.saveInTx(entities); return entities; } }); } /** * Rx version of {@link AbstractDao#update(Object)} returning an Observable. * Note that the Observable will emit the given entity back to its subscribers. */ @Experimental public Observable update(final T entity) { return wrap(new Callable() { @Override public T call() throws Exception { dao.update(entity); return entity; } }); } /** * Rx version of {@link AbstractDao#updateInTx(Iterable)} returning an Observable. * Note that the Observable will emit the given entities back to its subscribers. */ @Experimental public Observable> updateInTx(final Iterable entities) { return wrap(new Callable>() { @Override public Iterable call() throws Exception { dao.updateInTx(entities); return entities; } }); } /** * Rx version of {@link AbstractDao#updateInTx(Object[])} returning an Observable. * Note that the Observable will emit the given entities back to its subscribers. */ @Experimental public Observable updateInTx(final T... entities) { return wrap(new Callable() { @Override public Object[] call() throws Exception { dao.updateInTx(entities); return entities; } }); } /** * Rx version of {@link AbstractDao#delete(Object)} returning an Observable. */ @Experimental public Observable delete(final T entity) { return wrap(new Callable() { @Override public Void call() throws Exception { dao.delete(entity); return null; } }); } /** * Rx version of {@link AbstractDao#deleteByKey(Object)} returning an Observable. */ @Experimental public Observable deleteByKey(final K key) { return wrap(new Callable() { @Override public Void call() throws Exception { dao.deleteByKey(key); return null; } }); } /** * Rx version of {@link AbstractDao#deleteAll()} returning an Observable. */ @Experimental public Observable deleteAll() { return wrap(new Callable() { @Override public Void call() throws Exception { dao.deleteAll(); return null; } }); } /** * Rx version of {@link AbstractDao#deleteInTx(Iterable)} returning an Observable. */ @Experimental public Observable deleteInTx(final Iterable entities) { return wrap(new Callable() { @Override public Void call() throws Exception { dao.deleteInTx(entities); return null; } }); } /** * Rx version of {@link AbstractDao#deleteInTx(Object[])} returning an Observable. */ @Experimental public Observable deleteInTx(final T... entities) { return wrap(new Callable() { @Override public Void call() throws Exception { dao.deleteInTx(entities); return null; } }); } /** * Rx version of {@link AbstractDao#deleteByKeyInTx(Iterable)} returning an Observable. */ @Experimental public Observable deleteByKeyInTx(final Iterable keys) { return wrap(new Callable() { @Override public Void call() throws Exception { dao.deleteByKeyInTx(keys); return null; } }); } /** * Rx version of {@link AbstractDao#deleteByKeyInTx(Object[])} returning an Observable. */ @Experimental public Observable deleteByKeyInTx(final K... keys) { return wrap(new Callable() { @Override public Void call() throws Exception { dao.deleteByKeyInTx(keys); return null; } }); } /** * Rx version of {@link AbstractDao#count()} returning an Observable. */ @Experimental public Observable count() { return wrap(new Callable() { @Override public Long call() throws Exception { return dao.count(); } }); } /** * The plain DAO. */ @Experimental public AbstractDao getDao() { return dao; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/rx/RxQuery.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.rx; import org.greenrobot.greendao.annotation.apihint.Experimental; import org.greenrobot.greendao.query.LazyList; import org.greenrobot.greendao.query.Query; import java.util.List; import java.util.concurrent.Callable; import rx.Observable; import rx.Observable.OnSubscribe; import rx.Scheduler; import rx.Subscriber; import rx.exceptions.Exceptions; /** * Gets {@link org.greenrobot.greendao.query.Query} results in Rx fashion. */ @Experimental // TODO Pass parameters: currently, parameters are always set to their initial values because of forCurrentThread() public class RxQuery extends RxBase { private final Query query; public RxQuery(Query query) { this.query = query; } public RxQuery(Query query, Scheduler scheduler) { super(scheduler); this.query = query; } /** * Rx version of {@link Query#list()} returning an Observable. */ @Experimental public Observable> list() { return wrap(new Callable>() { @Override public List call() throws Exception { return query.forCurrentThread().list(); } }); } /** * Rx version of {@link Query#unique()} returning an Observable. */ @Experimental public Observable unique() { return wrap(new Callable() { @Override public T call() throws Exception { return query.forCurrentThread().unique(); } }); } /** * Emits the resulting entities one by one, producing them on the fly ("streaming" entities). * Unlike {@link #list()}, it does not wait for the query to gather all results. Thus, the first entities are * immediately available as soon the underlying database cursor has data. This approach may be more memory * efficient for large number of entities (or large entities) at the cost of additional overhead caused by a * per-entity delivery through Rx. */ public Observable oneByOne() { Observable observable = Observable.create(new OnSubscribe() { @Override public void call(Subscriber subscriber) { try { LazyList lazyList = query.forCurrentThread().listLazyUncached(); try { for (T entity : lazyList) { if (subscriber.isUnsubscribed()) { break; } subscriber.onNext(entity); } } finally { lazyList.close(); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } catch (Throwable e) { Exceptions.throwIfFatal(e); subscriber.onError(e); } } }); return wrap(observable); } // @Experimental // public Query getQuery() { // return query; // } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/rx/RxTransaction.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.rx; import org.greenrobot.greendao.AbstractDaoSession; import org.greenrobot.greendao.annotation.apihint.Experimental; import java.util.concurrent.Callable; import rx.Observable; import rx.Scheduler; /** * Allows to do transactions using Rx Observable. */ @Experimental public class RxTransaction extends RxBase { private final AbstractDaoSession daoSession; public RxTransaction(AbstractDaoSession daoSession) { this.daoSession = daoSession; } public RxTransaction(AbstractDaoSession daoSession, Scheduler scheduler) { super(scheduler); this.daoSession = daoSession; } /** * Rx version of {@link AbstractDaoSession#runInTx(Runnable)} returning an Observable. */ @Experimental public Observable run(final Runnable runnable) { return wrap(new Callable() { @Override public Void call() throws Exception { daoSession.runInTx(runnable); return null; } }); } /** * Rx version of {@link AbstractDaoSession#callInTx(Callable)} returning an Observable. */ @Experimental public Observable call(final Callable callable) { return wrap(new Callable() { @Override public T call() throws Exception { return daoSession.callInTx(callable); } }); } // Note: wrapping callInTxNoException does not make sense, because the Exception is handled by Rx anyway. @Experimental public AbstractDaoSession getDaoSession() { return daoSession; } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/rx/RxUtils.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.rx; import org.greenrobot.greendao.annotation.apihint.Internal; import java.util.concurrent.Callable; import rx.Observable; import rx.functions.Func0; @Internal class RxUtils { /** As of RxJava 1.1.7, Observable.fromCallable is still @Beta, so just in case... */ @Internal static Observable fromCallable(final Callable callable) { return Observable.defer(new Func0>() { @Override public Observable call() { T result; try { result = callable.call(); } catch (Exception e) { return Observable.error(e); } return Observable.just(result); } }); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/test/AbstractDaoSessionTest.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.test; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import android.database.sqlite.SQLiteDatabase; import org.greenrobot.greendao.AbstractDaoMaster; import org.greenrobot.greendao.AbstractDaoSession; import org.greenrobot.greendao.database.Database; /** * Base class for DAO (master) related testing. * * @author Markus * * @param * Type of a concrete DAO master */ public abstract class AbstractDaoSessionTest extends DbTest { private final Class daoMasterClass; protected T daoMaster; protected S daoSession; public AbstractDaoSessionTest(Class daoMasterClass) { this(daoMasterClass, true); } public AbstractDaoSessionTest(Class daoMasterClass, boolean inMemory) { super(inMemory); this.daoMasterClass = daoMasterClass; } @SuppressWarnings("unchecked") @Override protected void setUp() throws Exception { super.setUp(); try { Constructor constructor = daoMasterClass.getConstructor(Database.class); daoMaster = constructor.newInstance(db); Method createTableMethod = daoMasterClass.getMethod("createAllTables", Database.class, boolean.class); createTableMethod.invoke(null, db, false); } catch (Exception e) { throw new RuntimeException("Could not prepare DAO session test", e); } daoSession = (S) daoMaster.newSession(); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/test/AbstractDaoTest.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.test; import java.lang.reflect.Method; import android.database.sqlite.SQLiteDatabase; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoLog; import org.greenrobot.greendao.InternalUnitTestDaoAccess; import org.greenrobot.greendao.Property; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.identityscope.IdentityScope; /** * Base class for DAO related testing without any tests. Prepares an in-memory DB and DAO. * * @author Markus * * @param * DAO class * @param * Entity type of the DAO * @param * Key type of the DAO */ public abstract class AbstractDaoTest, T, K> extends DbTest { protected final Class daoClass; protected D dao; protected InternalUnitTestDaoAccess daoAccess; protected Property pkColumn; protected IdentityScope identityScopeForDao; public AbstractDaoTest(Class daoClass) { this(daoClass, true); } public AbstractDaoTest(Class daoClass, boolean inMemory) { super(inMemory); this.daoClass = daoClass; } public void setIdentityScopeBeforeSetUp(IdentityScope identityScope) { this.identityScopeForDao = identityScope; } @SuppressWarnings("unchecked") @Override protected void setUp() throws Exception { super.setUp(); try { setUpTableForDao(); daoAccess = new InternalUnitTestDaoAccess(db, (Class>) daoClass, identityScopeForDao); dao = (D) daoAccess.getDao(); } catch (Exception e) { throw new RuntimeException("Could not prepare DAO Test", e); } } protected void setUpTableForDao() throws Exception { try { Method createTableMethod = daoClass.getMethod("createTable", Database.class, boolean.class); createTableMethod.invoke(null, db, false); } catch (NoSuchMethodException e) { DaoLog.i("No createTable method"); } } protected void clearIdentityScopeIfAny() { if (identityScopeForDao != null) { identityScopeForDao.clear(); DaoLog.d("Identity scope cleared"); } else { DaoLog.d("No identity scope to clear"); } } protected void logTableDump() { logTableDump(dao.getTablename()); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/test/AbstractDaoTestLongPk.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.test; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoLog; /** * Base class for DAOs having a long/Long as a PK, which is quite common. * * @param DAO class * @param Entity type of the DAO * @author Markus */ public abstract class AbstractDaoTestLongPk, T> extends AbstractDaoTestSinglePk { public AbstractDaoTestLongPk(Class daoClass) { super(daoClass); } /** {@inheritDoc} */ protected Long createRandomPk() { return random.nextLong(); } public void testAssignPk() { if (daoAccess.isEntityUpdateable()) { T entity1 = createEntity(null); if (entity1 != null) { T entity2 = createEntity(null); dao.insert(entity1); dao.insert(entity2); Long pk1 = daoAccess.getKey(entity1); assertNotNull(pk1); Long pk2 = daoAccess.getKey(entity2); assertNotNull(pk2); assertFalse(pk1.equals(pk2)); assertNotNull(dao.load(pk1)); assertNotNull(dao.load(pk2)); } else { DaoLog.d("Skipping testAssignPk for " + daoClass + " (createEntity returned null for null key)"); } } else { DaoLog.d("Skipping testAssignPk for not updateable " + daoClass); } } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/test/AbstractDaoTestSinglePk.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.test; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.SQLException; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.DaoLog; import org.greenrobot.greendao.Property; import org.greenrobot.greendao.internal.SqlUtils; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Default tests for single-PK entities. * * @param DAO class * @param Entity type of the DAO * @param Key type of the DAO * @author Markus */ public abstract class AbstractDaoTestSinglePk, T, K> extends AbstractDaoTest { protected Set usedPks; private Property pkColumn; public AbstractDaoTestSinglePk(Class daoClass) { super(daoClass); usedPks = new HashSet(); } @Override protected void setUp() throws Exception { super.setUp(); Property[] columns = daoAccess.getProperties(); for (Property column : columns) { if (column.primaryKey) { if (pkColumn != null) { throw new RuntimeException("Test does not work with multiple PK columns"); } pkColumn = column; } } if (pkColumn == null) { throw new RuntimeException("Test does not work without a PK column"); } } public void testInsertAndLoad() { K pk = nextPk(); T entity = createEntity(pk); dao.insert(entity); assertEquals(pk, daoAccess.getKey(entity)); T entity2 = dao.load(pk); assertNotNull(entity2); assertEquals(daoAccess.getKey(entity), daoAccess.getKey(entity2)); } public void testInsertInTx() { dao.deleteAll(); List list = new ArrayList(); for (int i = 0; i < 20; i++) { list.add(createEntityWithRandomPk()); } dao.insertInTx(list); assertEquals(list.size(), dao.count()); } public void testCount() { dao.deleteAll(); assertEquals(0, dao.count()); dao.insert(createEntityWithRandomPk()); assertEquals(1, dao.count()); dao.insert(createEntityWithRandomPk()); assertEquals(2, dao.count()); } public void testInsertTwice() { K pk = nextPk(); T entity = createEntity(pk); dao.insert(entity); try { dao.insert(entity); fail("Inserting twice should not work"); } catch (SQLException expected) { // OK } } public void testInsertOrReplaceTwice() { T entity = createEntityWithRandomPk(); long rowId1 = dao.insert(entity); long rowId2 = dao.insertOrReplace(entity); if (dao.getPkProperty().type == Long.class) { assertEquals(rowId1, rowId2); } } public void testInsertOrReplaceInTx() { dao.deleteAll(); List listPartial = new ArrayList(); List listAll = new ArrayList(); for (int i = 0; i < 20; i++) { T entity = createEntityWithRandomPk(); if (i % 2 == 0) { listPartial.add(entity); } listAll.add(entity); } dao.insertOrReplaceInTx(listPartial); dao.insertOrReplaceInTx(listAll); assertEquals(listAll.size(), dao.count()); } public void testDelete() { K pk = nextPk(); dao.deleteByKey(pk); T entity = createEntity(pk); dao.insert(entity); assertNotNull(dao.load(pk)); dao.deleteByKey(pk); assertNull(dao.load(pk)); } public void testDeleteAll() { List entityList = new ArrayList(); for (int i = 0; i < 10; i++) { T entity = createEntityWithRandomPk(); entityList.add(entity); } dao.insertInTx(entityList); dao.deleteAll(); assertEquals(0, dao.count()); for (T entity : entityList) { K key = daoAccess.getKey(entity); assertNotNull(key); assertNull(dao.load(key)); } } public void testDeleteInTx() { List entityList = new ArrayList(); for (int i = 0; i < 10; i++) { T entity = createEntityWithRandomPk(); entityList.add(entity); } dao.insertInTx(entityList); List entitiesToDelete = new ArrayList(); entitiesToDelete.add(entityList.get(0)); entitiesToDelete.add(entityList.get(3)); entitiesToDelete.add(entityList.get(4)); entitiesToDelete.add(entityList.get(8)); dao.deleteInTx(entitiesToDelete); assertEquals(entityList.size() - entitiesToDelete.size(), dao.count()); for (T deletedEntity : entitiesToDelete) { K key = daoAccess.getKey(deletedEntity); assertNotNull(key); assertNull(dao.load(key)); } } public void testDeleteByKeyInTx() { List entityList = new ArrayList(); for (int i = 0; i < 10; i++) { T entity = createEntityWithRandomPk(); entityList.add(entity); } dao.insertInTx(entityList); List keysToDelete = new ArrayList(); keysToDelete.add(daoAccess.getKey(entityList.get(0))); keysToDelete.add(daoAccess.getKey(entityList.get(3))); keysToDelete.add(daoAccess.getKey(entityList.get(4))); keysToDelete.add(daoAccess.getKey(entityList.get(8))); dao.deleteByKeyInTx(keysToDelete); assertEquals(entityList.size() - keysToDelete.size(), dao.count()); for (K key : keysToDelete) { assertNotNull(key); assertNull(dao.load(key)); } } public void testRowId() { T entity1 = createEntityWithRandomPk(); T entity2 = createEntityWithRandomPk(); long rowId1 = dao.insert(entity1); long rowId2 = dao.insert(entity2); assertTrue(rowId1 != rowId2); } public void testLoadAll() { dao.deleteAll(); List list = new ArrayList(); for (int i = 0; i < 15; i++) { T entity = createEntity(nextPk()); list.add(entity); } dao.insertInTx(list); List loaded = dao.loadAll(); assertEquals(list.size(), loaded.size()); } public void testQuery() { dao.insert(createEntityWithRandomPk()); K pkForQuery = nextPk(); dao.insert(createEntity(pkForQuery)); dao.insert(createEntityWithRandomPk()); String where = "WHERE " + dao.getPkColumns()[0] + "=?"; List list = dao.queryRaw(where, pkForQuery.toString()); assertEquals(1, list.size()); assertEquals(pkForQuery, daoAccess.getKey(list.get(0))); } public void testUpdate() { dao.deleteAll(); T entity = createEntityWithRandomPk(); dao.insert(entity); dao.update(entity); assertEquals(1, dao.count()); } public void testReadWithOffset() { K pk = nextPk(); T entity = createEntity(pk); dao.insert(entity); Cursor cursor = queryWithDummyColumnsInFront(5, "42", pk); try { T entity2 = daoAccess.readEntity(cursor, 5); assertEquals(pk, daoAccess.getKey(entity2)); } finally { cursor.close(); } } public void testLoadPkWithOffset() { runLoadPkTest(10); } public void testLoadPk() { runLoadPkTest(0); } public void testSave() { if(!checkKeyIsNullable()) { return; } dao.deleteAll(); T entity = createEntity(null); if (entity != null) { dao.save(entity); dao.save(entity); assertEquals(1, dao.count()); } } public void testSaveInTx() { if(!checkKeyIsNullable()) { return; } dao.deleteAll(); List listPartial = new ArrayList(); List listAll = new ArrayList(); for (int i = 0; i < 20; i++) { T entity = createEntity(null); if (i % 2 == 0) { listPartial.add(entity); } listAll.add(entity); } dao.saveInTx(listPartial); dao.saveInTx(listAll); assertEquals(listAll.size(), dao.count()); } protected void runLoadPkTest(int offset) { K pk = nextPk(); T entity = createEntity(pk); dao.insert(entity); Cursor cursor = queryWithDummyColumnsInFront(offset, "42", pk); try { K pk2 = daoAccess.readKey(cursor, offset); assertEquals(pk, pk2); } finally { cursor.close(); } } protected Cursor queryWithDummyColumnsInFront(int dummyCount, String valueForColumn, K pk) { StringBuilder builder = new StringBuilder("SELECT "); for (int i = 0; i < dummyCount; i++) { builder.append(valueForColumn).append(","); } SqlUtils.appendColumns(builder, "T", dao.getAllColumns()).append(" FROM "); builder.append('"').append(dao.getTablename()).append('"').append(" T"); if (pk != null) { builder.append(" WHERE "); assertEquals(1, dao.getPkColumns().length); builder.append(dao.getPkColumns()[0]).append("="); DatabaseUtils.appendValueToSql(builder, pk); } String select = builder.toString(); Cursor cursor = db.rawQuery(select, null); assertTrue(cursor.moveToFirst()); try { for (int i = 0; i < dummyCount; i++) { assertEquals(valueForColumn, cursor.getString(i)); } if (pk != null) { assertEquals(1, cursor.getCount()); } } catch (RuntimeException ex) { cursor.close(); throw ex; } return cursor; } protected boolean checkKeyIsNullable() { if (createEntity(null) == null) { DaoLog.d("Test is not available for entities with non-null keys"); return false; } return true; } /** Provides a collision free PK () not returned before in the current test. */ protected K nextPk() { for (int i = 0; i < 100000; i++) { K pk = createRandomPk(); if (usedPks.add(pk)) { return pk; } } throw new IllegalStateException("Could not find a new PK"); } protected T createEntityWithRandomPk() { return createEntity(nextPk()); } /** K does not have to be collision free, check nextPk for collision free PKs. */ protected abstract K createRandomPk(); /** * Creates an insertable entity. If the given key is null, but the entity's PK is not null the method must return * null. */ protected abstract T createEntity(K key); } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/test/AbstractDaoTestStringPk.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.test; import org.greenrobot.greendao.AbstractDao; /** * Base class for DAOs having a String as a PK. * * @author Markus * * @param * DAO class * @param * Entity type of the DAO */ public abstract class AbstractDaoTestStringPk, T> extends AbstractDaoTestSinglePk { public AbstractDaoTestStringPk(Class daoClass) { super(daoClass); } @Override protected String createRandomPk() { int len = 1 + random.nextInt(30); StringBuilder builder = new StringBuilder(); for (int i = 0; i < len; i++) { char c = (char) ('a' + random.nextInt('z' - 'a')); builder.append(c); } return builder.toString(); } } ================================================ FILE: DaoCore/src/main/java/org/greenrobot/greendao/test/DbTest.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * 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. */ package org.greenrobot.greendao.test; import android.app.Application; import android.app.Instrumentation; import android.database.sqlite.SQLiteDatabase; import android.test.AndroidTestCase; import org.greenrobot.greendao.DaoLog; import org.greenrobot.greendao.DbUtils; import org.greenrobot.greendao.database.StandardDatabase; import org.greenrobot.greendao.database.Database; import java.util.Random; /** * Base class for database related testing, which prepares an in-memory or an file-based DB (using the test {@link * android.content.Context}). Also, offers some convenience methods to create new {@link Application} objects similar * to {@link android.test.ApplicationTestCase}. *

* Unlike ApplicationTestCase, this class should behave more correctly when you call {@link #createApplication(Class)} * during {@link #setUp()}: {@link android.test.ApplicationTestCase#testApplicationTestCaseSetUpProperly()} leaves * Application objects un-terminated. * * @author Markus */ public abstract class DbTest extends AndroidTestCase { public static final String DB_NAME = "greendao-unittest-db.temp"; protected final Random random; protected final boolean inMemory; protected Database db; private Application application; public DbTest() { this(true); } public DbTest(boolean inMemory) { this.inMemory = inMemory; random = new Random(); } @Override protected void setUp() throws Exception { super.setUp(); db = createDatabase(); } /** Returns a prepared application with the onCreate method already called. */ public T createApplication(Class appClass) { assertNull("Application already created", application); T app; try { app = (T) Instrumentation.newApplication(appClass, getContext()); } catch (Exception e) { throw new RuntimeException("Could not create application " + appClass, e); } app.onCreate(); application = app; return app; } /** Terminates a previously created application. Also called by {@link #tearDown()} if needed. */ public void terminateApplication() { assertNotNull("Application not yet created", application); application.onTerminate(); application = null; } /** Gets the previously created application. */ public T getApplication() { assertNotNull("Application not yet created", application); return (T) application; } /** May be overriden by sub classes to set up a different db. */ protected Database createDatabase() { SQLiteDatabase sqLiteDatabase; if (inMemory) { sqLiteDatabase = SQLiteDatabase.create(null); } else { getContext().deleteDatabase(DB_NAME); sqLiteDatabase = getContext().openOrCreateDatabase(DB_NAME, 0, null); } return new StandardDatabase(sqLiteDatabase); } @Override /** Closes the db, and terminates an application, if one was created before. */ protected void tearDown() throws Exception { if (application != null) { terminateApplication(); } db.close(); if (!inMemory) { getContext().deleteDatabase(DB_NAME); } super.tearDown(); } protected void logTableDump(String tablename) { if (db instanceof StandardDatabase) { DbUtils.logTableDump(((StandardDatabase) db).getSQLiteDatabase(), tablename); } else { DaoLog.w("Table dump unsupported for " + db); } } } ================================================ FILE: DaoGenerator/.freemarker-ide.xml ================================================ ================================================ FILE: DaoGenerator/.gitignore ================================================ /gradle.properties ================================================ FILE: DaoGenerator/build.gradle ================================================ apply plugin: 'java' group = 'org.greenrobot' archivesBaseName = 'greendao-generator' version = rootProject.version sourceCompatibility = 1.7 repositories { mavenCentral() } dependencies { compile 'org.freemarker:freemarker:2.3.29' testImplementation 'junit:junit:4.12' } sourceSets { main { java { srcDir 'src' } resources { srcDir 'src-template' } } test { java { srcDir 'src-test' } } } test { doFirst { mkdir 'test-out' } doLast { delete 'test-out' } } apply from: rootProject.file("gradle/publish.gradle") javadoc { failOnError = false title = "greenDAO Generator ${version} API" // Unfinished APIs: excludes = ['org/greenrobot/daogenerator/Query*'] options.bottom = 'Available under the GPLv3 - Copyright © 2011-2020 greenrobot.org. All Rights Reserved.' doLast { copy { from '../javadoc-style/' into "build/docs/javadoc/" } } } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from 'build/docs/javadoc' } task sourcesJar(type: Jar) { from sourceSets.main.allSource classifier = 'sources' } artifacts { // jar added by Java plugin. archives javadocJar archives sourcesJar } uploadArchives { repositories { mavenDeployer { // Basic definitions are defined in root project pom.project { name 'greenDAO Generator' description 'Code generator for greenDAO, the light and fast ORM for Android' licenses { license { name 'GNU General Public License, Version 3' url 'http://www.gnu.org/licenses/gpl.txt' distribution 'repo' } } } } } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/ContentProvider.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import java.util.List; @SuppressWarnings("unused") public class ContentProvider { private final List entities; private String authority; private String basePath; private String className; private String javaPackage; private boolean readOnly; private Schema schema; public ContentProvider(Schema schema, List entities) { this.schema = schema; this.entities = entities; } public String getAuthority() { return authority; } public void setAuthority(String authority) { this.authority = authority; } public String getBasePath() { return basePath; } public void setBasePath(String basePath) { this.basePath = basePath; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getJavaPackage() { return javaPackage; } public void setJavaPackage(String javaPackage) { this.javaPackage = javaPackage; } public boolean isReadOnly() { return readOnly; } public void readOnly() { this.readOnly = true; } public List getEntities() { return entities; } public void init2ndPass() { if (authority == null) { authority = schema.getDefaultJavaPackage() + ".provider"; } if (basePath == null) { basePath = ""; } if (className == null) { className = entities.get(0).getClassName() + "ContentProvider"; } if (javaPackage == null) { javaPackage = schema.getDefaultJavaPackage(); } } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/DaoGenerator.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateNotFoundException; /** * Once you have your model created, use this class to generate entities and DAOs. * * @author Markus */ public class DaoGenerator { private Pattern patternKeepIncludes; private Pattern patternKeepFields; private Pattern patternKeepMethods; private Template templateDao; private Template templateDaoMaster; private Template templateDaoSession; private Template templateEntity; private Template templateDaoUnitTest; private Template templateContentProvider; public DaoGenerator() throws IOException { System.out.println("greenDAO Generator"); System.out.println("Copyright 2011-2016 Markus Junginger, greenrobot.de. Licensed under GPL V3."); System.out.println("This program comes with ABSOLUTELY NO WARRANTY"); patternKeepIncludes = compilePattern("INCLUDES"); patternKeepFields = compilePattern("FIELDS"); patternKeepMethods = compilePattern("METHODS"); Configuration config = getConfiguration("dao.ftl"); templateDao = config.getTemplate("dao.ftl"); templateDaoMaster = config.getTemplate("dao-master.ftl"); templateDaoSession = config.getTemplate("dao-session.ftl"); templateEntity = config.getTemplate("entity.ftl"); templateDaoUnitTest = config.getTemplate("dao-unit-test.ftl"); templateContentProvider = config.getTemplate("content-provider.ftl"); } private Configuration getConfiguration(String probingTemplate) throws IOException { Configuration config = new Configuration(Configuration.VERSION_2_3_29); config.setClassForTemplateLoading(getClass(), "/"); try { config.getTemplate(probingTemplate); } catch (TemplateNotFoundException e) { // When running from an IDE like IntelliJ, class loading resources may fail for some reason (Gradle is OK) // Working dir is module dir File dir = new File("src/main/resources/"); if (!dir.exists()) { // Working dir is base module dir dir = new File("DaoGenerator/src/main/resources/"); } if (dir.exists() && new File(dir, probingTemplate).exists()) { config.setDirectoryForTemplateLoading(dir); config.getTemplate(probingTemplate); } else { throw e; } } return config; } private Pattern compilePattern(String sectionName) { int flags = Pattern.DOTALL | Pattern.MULTILINE; return Pattern.compile(".*^\\s*?//\\s*?KEEP " + sectionName + ".*?\n(.*?)^\\s*// KEEP " + sectionName + " END.*?\n", flags); } /** Generates all entities and DAOs for the given schema. */ public void generateAll(Schema schema, String outDir) throws Exception { generateAll(schema, outDir, null, null); } /** Generates all entities and DAOs for the given schema. */ public void generateAll(Schema schema, String outDir, String outDirEntity, String outDirTest) throws Exception { long start = System.currentTimeMillis(); File outDirFile = toFileForceExists(outDir); File outDirEntityFile = outDirEntity != null ? toFileForceExists(outDirEntity) : outDirFile; File outDirTestFile = outDirTest != null ? toFileForceExists(outDirTest) : null; schema.init2ndPass(); schema.init3rdPass(); System.out.println("Processing schema version " + schema.getVersion() + "..."); List entities = schema.getEntities(); for (Entity entity : entities) { generate(templateDao, outDirFile, entity.getJavaPackageDao(), entity.getClassNameDao(), schema, entity); if (!entity.isProtobuf() && !entity.isSkipGeneration()) { generate(templateEntity, outDirEntityFile, entity.getJavaPackage(), entity.getClassName(), schema, entity); } if (outDirTestFile != null && !entity.isSkipGenerationTest()) { String javaPackageTest = entity.getJavaPackageTest(); String classNameTest = entity.getClassNameTest(); File javaFilename = toJavaFilename(outDirTestFile, javaPackageTest, classNameTest); if (!javaFilename.exists()) { generate(templateDaoUnitTest, outDirTestFile, javaPackageTest, classNameTest, schema, entity); } else { System.out.println("Skipped " + javaFilename.getCanonicalPath()); } } for (ContentProvider contentProvider : entity.getContentProviders()) { Map additionalObjectsForTemplate = new HashMap<>(); additionalObjectsForTemplate.put("contentProvider", contentProvider); generate(templateContentProvider, outDirFile, entity.getJavaPackage(), entity.getClassName() + "ContentProvider", schema, entity, additionalObjectsForTemplate); } } generate(templateDaoMaster, outDirFile, schema.getDefaultJavaPackageDao(), schema.getPrefix() + "DaoMaster", schema, null); generate(templateDaoSession, outDirFile, schema.getDefaultJavaPackageDao(), schema.getPrefix() + "DaoSession", schema, null); long time = System.currentTimeMillis() - start; System.out.println("Processed " + entities.size() + " entities in " + time + "ms"); } protected File toFileForceExists(String filename) throws IOException { File file = new File(filename); if (!file.exists()) { throw new IOException(filename + " does not exist. This check is to prevent accidental file generation into a wrong path."); } return file; } private void generate(Template template, File outDirFile, String javaPackage, String javaClassName, Schema schema, Entity entity) throws Exception { generate(template, outDirFile, javaPackage, javaClassName, schema, entity, null); } private void generate(Template template, File outDirFile, String javaPackage, String javaClassName, Schema schema, Entity entity, Map additionalObjectsForTemplate) throws Exception { Map root = new HashMap<>(); root.put("schema", schema); root.put("entity", entity); if (additionalObjectsForTemplate != null) { root.putAll(additionalObjectsForTemplate); } try { File file = toJavaFilename(outDirFile, javaPackage, javaClassName); //noinspection ResultOfMethodCallIgnored file.getParentFile().mkdirs(); if (entity != null && entity.getHasKeepSections()) { checkKeepSections(file, root); } Writer writer = new FileWriter(file); try { template.process(root, writer); writer.flush(); System.out.println("Written " + file.getCanonicalPath()); } finally { writer.close(); } } catch (Exception ex) { System.err.println("Data map for template: " + root); System.err.println("Error while generating " + javaPackage + "." + javaClassName + " (" + outDirFile.getCanonicalPath() + ")"); throw ex; } } private void checkKeepSections(File file, Map root) { if (file.exists()) { try { String contents = new String(DaoUtil.readAllBytes(file)); Matcher matcher; matcher = patternKeepIncludes.matcher(contents); if (matcher.matches()) { root.put("keepIncludes", matcher.group(1)); } matcher = patternKeepFields.matcher(contents); if (matcher.matches()) { root.put("keepFields", matcher.group(1)); } matcher = patternKeepMethods.matcher(contents); if (matcher.matches()) { root.put("keepMethods", matcher.group(1)); } } catch (IOException e) { e.printStackTrace(); } } } protected File toJavaFilename(File outDirFile, String javaPackage, String javaClassName) { String packageSubPath = javaPackage.replace('.', '/'); File packagePath = new File(outDirFile, packageSubPath); return new File(packagePath, javaClassName + ".java"); } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/DaoUtil.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** Internal API */ public class DaoUtil { public static String dbName(String javaName) { StringBuilder builder = new StringBuilder(javaName); for (int i = 1; i < builder.length(); i++) { boolean lastWasUpper = Character.isUpperCase(builder.charAt(i - 1)); boolean isUpper = Character.isUpperCase(builder.charAt(i)); if (isUpper && !lastWasUpper) { builder.insert(i, '_'); i++; } } return builder.toString().toUpperCase(); } public static String getClassnameFromFullyQualified(String clazz) { int index = clazz.lastIndexOf('.'); if (index != -1) { return clazz.substring(index + 1); } else { return clazz; } } public static String capFirst(String string) { return Character.toUpperCase(string.charAt(0)) + (string.length() > 1 ? string.substring(1) : ""); } public static String getPackageFromFullyQualified(String clazz) { int index = clazz.lastIndexOf('.'); if (index != -1) { return clazz.substring(0, index); } else { return null; } } public static byte[] readAllBytes(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); copyAllBytes(in, out); return out.toByteArray(); } public static byte[] readAllBytes(File file) throws IOException { FileInputStream is = new FileInputStream(file); try { return DaoUtil.readAllBytes(is); } finally { is.close(); } } public static byte[] readAllBytes(String filename) throws IOException { FileInputStream is = new FileInputStream(filename); try { return DaoUtil.readAllBytes(is); } finally { is.close(); } } /** * Copies all available data from in to out without closing any stream. * * @return number of bytes copied */ public static int copyAllBytes(InputStream in, OutputStream out) throws IOException { int byteCount = 0; byte[] buffer = new byte[4096]; while (true) { int read = in.read(buffer); if (read == -1) { break; } out.write(buffer, 0, read); byteCount += read; } return byteCount; } public static String checkConvertToJavaDoc(String javaDoc, String indent) { if (javaDoc != null && !javaDoc.trim().startsWith("/**")) { javaDoc = javaDoc.replace("\n", "\n" + indent + " * "); javaDoc = indent + "/**\n" + indent + " * " + javaDoc + "\n" + indent + " */"; } return javaDoc; } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/Entity.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import org.greenrobot.greendao.generator.Property.PropertyBuilder; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; /** * Model class for an entity: a Java data object mapped to a data base table. A new entity is added to a {@link Schema} * by the method {@link Schema#addEntity(String)} (there is no public constructor for {@link Entity} itself).
*
Use the various addXXX methods to add entity properties, indexes, and relations to other entities (addToOne, * addToMany).

There are further configuration possibilities:

  • {@link * Entity#implementsInterface(String...)} and {@link #implementsSerializable()} to specify interfaces the entity will * implement
  • {@link #setSuperclass(String)} to specify a class of which the entity will extend from
  • *
  • Various setXXX methods
* * @see Modelling Entities (Documentation page) * @see Relations (Documentation page) */ @SuppressWarnings("unused") public class Entity { private final Schema schema; private final String className; private final List properties; private List propertiesColumns; private final List propertiesPk; private final List propertiesNonPk; private final Set propertyNames; private final List indexes; private final List multiIndexes; private final List toOneRelations; private final List toManyRelations; private final List incomingToManyRelations; private final Collection additionalImportsEntity; private final Collection additionalImportsDao; private final List interfacesToImplement; private final List contentProviders; private String dbName; private boolean nonDefaultDbName; private String classNameDao; private String classNameTest; private String javaPackage; private String javaPackageDao; private String javaPackageTest; private Property pkProperty; private String pkType; private String superclass; private String javaDoc; private String codeBeforeClass; private boolean protobuf; private boolean constructors; private boolean skipGeneration; private boolean skipGenerationTest; private boolean skipCreationInDb; private Boolean active; private Boolean hasKeepSections; Entity(Schema schema, String className) { this.schema = schema; this.className = className; properties = new ArrayList<>(); propertiesPk = new ArrayList<>(); propertiesNonPk = new ArrayList<>(); propertyNames = new HashSet<>(); indexes = new ArrayList<>(); multiIndexes = new ArrayList<>(); toOneRelations = new ArrayList<>(); toManyRelations = new ArrayList<>(); incomingToManyRelations = new ArrayList<>(); additionalImportsEntity = new TreeSet<>(); additionalImportsDao = new TreeSet<>(); interfacesToImplement = new ArrayList<>(); contentProviders = new ArrayList<>(); constructors = true; } public PropertyBuilder addBooleanProperty(String propertyName) { return addProperty(PropertyType.Boolean, propertyName); } public PropertyBuilder addByteProperty(String propertyName) { return addProperty(PropertyType.Byte, propertyName); } public PropertyBuilder addShortProperty(String propertyName) { return addProperty(PropertyType.Short, propertyName); } public PropertyBuilder addIntProperty(String propertyName) { return addProperty(PropertyType.Int, propertyName); } public PropertyBuilder addLongProperty(String propertyName) { return addProperty(PropertyType.Long, propertyName); } public PropertyBuilder addFloatProperty(String propertyName) { return addProperty(PropertyType.Float, propertyName); } public PropertyBuilder addDoubleProperty(String propertyName) { return addProperty(PropertyType.Double, propertyName); } public PropertyBuilder addByteArrayProperty(String propertyName) { return addProperty(PropertyType.ByteArray, propertyName); } public PropertyBuilder addStringProperty(String propertyName) { return addProperty(PropertyType.String, propertyName); } public PropertyBuilder addDateProperty(String propertyName) { return addProperty(PropertyType.Date, propertyName); } public PropertyBuilder addProperty(PropertyType propertyType, String propertyName) { if (!propertyNames.add(propertyName)) { throw new RuntimeException("Property already defined: " + propertyName); } PropertyBuilder builder = new PropertyBuilder(schema, this, propertyType, propertyName); properties.add(builder.getProperty()); return builder; } /** Adds a standard _id column required by standard Android classes, e.g. list adapters. */ public PropertyBuilder addIdProperty() { PropertyBuilder builder = addLongProperty("id"); builder.dbName("_id").primaryKey(); return builder; } /** Adds a to-many relationship; the target entity is joined to the PK property of this entity (typically the ID). */ public ToMany addToMany(Entity target, Property targetProperty) { Property[] targetProperties = {targetProperty}; return addToMany(null, target, targetProperties); } /** * Convenience method for {@link Entity#addToMany(Entity, Property)} with a subsequent call to {@link * ToMany#setName(String)}. */ public ToMany addToMany(Entity target, Property targetProperty, String name) { ToMany toMany = addToMany(target, targetProperty); toMany.setName(name); return toMany; } /** * Add a to-many relationship; the target entity is joined using the given target property (of the target entity) * and given source property (of this entity). */ public ToMany addToMany(Property sourceProperty, Entity target, Property targetProperty) { Property[] sourceProperties = {sourceProperty}; Property[] targetProperties = {targetProperty}; return addToMany(sourceProperties, target, targetProperties); } public ToMany addToMany(Property[] sourceProperties, Entity target, Property[] targetProperties) { if (protobuf) { throw new IllegalStateException("Protobuf entities do not support relations, currently"); } ToMany toMany = new ToMany(schema, this, sourceProperties, target, targetProperties); toManyRelations.add(toMany); target.incomingToManyRelations.add(toMany); return toMany; } public ToManyWithJoinEntity addToMany(Entity target, Entity joinEntity, Property id1, Property id2) { ToManyWithJoinEntity toMany = new ToManyWithJoinEntity(schema, this, target, joinEntity, id1, id2); toManyRelations.add(toMany); target.incomingToManyRelations.add(toMany); return toMany; } /** * Adds a to-one relationship to the given target entity using the given given foreign key property (which belongs * to this entity). */ public ToOne addToOne(Entity target, Property fkProperty) { if (protobuf) { throw new IllegalStateException("Protobuf entities do not support realtions, currently"); } Property[] fkProperties = {fkProperty}; ToOne toOne = new ToOne(schema, this, target, fkProperties, true); toOneRelations.add(toOne); return toOne; } /** Convenience for {@link #addToOne(Entity, Property)} with a subsequent call to {@link ToOne#setName(String)}. */ public ToOne addToOne(Entity target, Property fkProperty, String name) { ToOne toOne = addToOne(target, fkProperty); toOne.setName(name); return toOne; } public ToOne addToOneWithoutProperty(String name, Entity target, String fkColumnName) { return addToOneWithoutProperty(name, target, fkColumnName, false, false); } public ToOne addToOneWithoutProperty(String name, Entity target, String fkColumnName, boolean notNull, boolean unique) { PropertyBuilder propertyBuilder = new PropertyBuilder(schema, this, null, name); if (notNull) { propertyBuilder.notNull(); } if (unique) { propertyBuilder.unique(); } propertyBuilder.dbName(fkColumnName); Property column = propertyBuilder.getProperty(); Property[] fkColumns = {column}; ToOne toOne = new ToOne(schema, this, target, fkColumns, false); toOne.setName(name); toOneRelations.add(toOne); return toOne; } protected void addIncomingToMany(ToMany toMany) { incomingToManyRelations.add(toMany); } public ContentProvider addContentProvider() { List entities = new ArrayList<>(); entities.add(this); ContentProvider contentProvider = new ContentProvider(schema, entities); contentProviders.add(contentProvider); return contentProvider; } /** Adds a new index to the entity. */ public Entity addIndex(Index index) { indexes.add(index); return this; } public Entity addImport(String additionalImport) { additionalImportsEntity.add(additionalImport); return this; } /** The entity is represented by a protocol buffers object. Requires some special actions like using builders. */ Entity useProtobuf() { protobuf = true; return this; } public boolean isProtobuf() { return protobuf; } public Schema getSchema() { return schema; } public String getDbName() { return dbName; } @Deprecated /** * @deprecated Use setDbName */ public void setTableName(String tableName) { setDbName(tableName); } public void setDbName(String dbName) { this.dbName = dbName; this.nonDefaultDbName = dbName != null; } public String getClassName() { return className; } public List getProperties() { return properties; } public List getPropertiesColumns() { return propertiesColumns; } public String getJavaPackage() { return javaPackage; } public void setJavaPackage(String javaPackage) { this.javaPackage = javaPackage; } public String getJavaPackageDao() { return javaPackageDao; } public void setJavaPackageDao(String javaPackageDao) { this.javaPackageDao = javaPackageDao; } public String getClassNameDao() { return classNameDao; } public void setClassNameDao(String classNameDao) { this.classNameDao = classNameDao; } public String getClassNameTest() { return classNameTest; } public void setClassNameTest(String classNameTest) { this.classNameTest = classNameTest; } public String getJavaPackageTest() { return javaPackageTest; } public void setJavaPackageTest(String javaPackageTest) { this.javaPackageTest = javaPackageTest; } /** Internal property used by templates, don't use during entity definition. */ public List getPropertiesPk() { return propertiesPk; } /** Internal property used by templates, don't use during entity definition. */ public List getPropertiesNonPk() { return propertiesNonPk; } /** Internal property used by templates, don't use during entity definition. */ public Property getPkProperty() { return pkProperty; } public List getIndexes() { return indexes; } /** Internal property used by templates, don't use during entity definition. */ public String getPkType() { return pkType; } public boolean isConstructors() { return constructors; } /** Flag to define if constructors should be generated. */ public void setConstructors(boolean constructors) { this.constructors = constructors; } public boolean isSkipGeneration() { return skipGeneration; } /** * Flag if the entity's code generation should be skipped. E.g. if you need to change the class after initial * generation. */ public void setSkipGeneration(boolean skipGeneration) { this.skipGeneration = skipGeneration; } @Deprecated /** * @deprecated Use setSkipCreationInDb */ public void setSkipTableCreation(boolean skipTableCreation) { setSkipCreationInDb(skipTableCreation); } /** Flag if CREATE and DROP TABLE scripts should be skipped in Dao. */ public void setSkipCreationInDb(boolean skipCreationInDb) { this.skipCreationInDb = skipCreationInDb; } public boolean isSkipCreationInDb() { return skipCreationInDb; } public boolean isSkipGenerationTest() { return skipGenerationTest; } public void setSkipGenerationTest(boolean skipGenerationTest) { this.skipGenerationTest = skipGenerationTest; } public List getToOneRelations() { return toOneRelations; } public List getToManyRelations() { return toManyRelations; } public List getIncomingToManyRelations() { return incomingToManyRelations; } /** * Entities with relations are active, but this method allows to make the entities active even if it does not have * relations. */ public void setActive(Boolean active) { this.active = active; } public Boolean getActive() { return active; } public Boolean getHasKeepSections() { return hasKeepSections; } public Collection getAdditionalImportsEntity() { return additionalImportsEntity; } public Collection getAdditionalImportsDao() { return additionalImportsDao; } public void setHasKeepSections(Boolean hasKeepSections) { this.hasKeepSections = hasKeepSections; } public List getInterfacesToImplement() { return interfacesToImplement; } public List getContentProviders() { return contentProviders; } public void implementsInterface(String... interfaces) { for (String interfaceToImplement : interfaces) { if (interfacesToImplement.contains(interfaceToImplement)) { throw new RuntimeException("Interface defined more than once: " + interfaceToImplement); } interfacesToImplement.add(interfaceToImplement); } } public void implementsSerializable() { interfacesToImplement.add("java.io.Serializable"); } public String getSuperclass() { return superclass; } public void setSuperclass(String classToExtend) { this.superclass = classToExtend; } public String getJavaDoc() { return javaDoc; } public void setJavaDoc(String javaDoc) { this.javaDoc = DaoUtil.checkConvertToJavaDoc(javaDoc, ""); } public String getCodeBeforeClass() { return codeBeforeClass; } public void setCodeBeforeClass(String codeBeforeClass) { this.codeBeforeClass = codeBeforeClass; } void init2ndPass() { init2ndPassNamesWithDefaults(); for (int i = 0; i < properties.size(); i++) { Property property = properties.get(i); property.setOrdinal(i); property.init2ndPass(); if (property.isPrimaryKey()) { propertiesPk.add(property); } else { propertiesNonPk.add(property); } } for (int i = 0; i < indexes.size(); i++) { final Index index = indexes.get(i); final int propertiesSize = index.getProperties().size(); if (propertiesSize == 1) { final Property property = index.getProperties().get(0); property.setIndex(index); } else if (propertiesSize > 1) { multiIndexes.add(index); } } if (propertiesPk.size() == 1) { pkProperty = propertiesPk.get(0); pkType = schema.mapToJavaTypeNullable(pkProperty.getPropertyType()); } else { pkType = "Void"; } propertiesColumns = new ArrayList<>(properties); for (ToOne toOne : toOneRelations) { toOne.init2ndPass(); Property[] fkProperties = toOne.getFkProperties(); for (Property fkProperty : fkProperties) { if (!propertiesColumns.contains(fkProperty)) { propertiesColumns.add(fkProperty); } } } for (ToManyBase toMany : toManyRelations) { toMany.init2ndPass(); // Source Properties may not be virtual, so we do not need the following code: // for (Property sourceProperty : toMany.getSourceProperties()) { // if (!propertiesColumns.contains(sourceProperty)) { // propertiesColumns.add(sourceProperty); // } // } } if (active == null) { active = schema.isUseActiveEntitiesByDefault(); } active |= !toOneRelations.isEmpty() || !toManyRelations.isEmpty(); if (hasKeepSections == null) { hasKeepSections = schema.isHasKeepSectionsByDefault(); } init2ndPassIndexNamesWithDefaults(); for (ContentProvider contentProvider : contentProviders) { contentProvider.init2ndPass(); } } protected void init2ndPassNamesWithDefaults() { if (dbName == null) { dbName = DaoUtil.dbName(className); nonDefaultDbName = false; } if (classNameDao == null) { classNameDao = className + "Dao"; } if (classNameTest == null) { classNameTest = className + "Test"; } if (javaPackage == null) { javaPackage = schema.getDefaultJavaPackage(); } if (javaPackageDao == null) { javaPackageDao = schema.getDefaultJavaPackageDao(); if (javaPackageDao == null) { javaPackageDao = javaPackage; } } if (javaPackageTest == null) { javaPackageTest = schema.getDefaultJavaPackageTest(); if (javaPackageTest == null) { javaPackageTest = javaPackage; } } } protected void init2ndPassIndexNamesWithDefaults() { for (int i = 0; i < indexes.size(); i++) { Index index = indexes.get(i); if (index.getName() == null) { String indexName = "IDX_" + getDbName(); List properties = index.getProperties(); for (int j = 0; j < properties.size(); j++) { Property property = properties.get(j); indexName += "_" + property.getDbName(); if ("DESC".equalsIgnoreCase(index.getPropertiesOrder().get(j))) { indexName += "_DESC"; } } // TODO can this get too long? how to shorten reliably without depending on the order (i) index.setDefaultName(indexName); } } } void init3rdPass() { for (Property property : properties) { property.init3ndPass(); } init3rdPassRelations(); init3rdPassAdditionalImports(); } private void init3rdPassRelations() { Set toOneNames = new HashSet<>(); for (ToOne toOne : toOneRelations) { toOne.init3ndPass(); if (!toOneNames.add(toOne.getName().toLowerCase())) { throw new RuntimeException("Duplicate name for " + toOne); } } Set toManyNames = new HashSet<>(); for (ToManyBase toMany : toManyRelations) { toMany.init3rdPass(); if (toMany instanceof ToMany) { Entity targetEntity = toMany.getTargetEntity(); for (Property targetProperty : ((ToMany) toMany).getTargetProperties()) { if (!targetEntity.propertiesColumns.contains(targetProperty)) { targetEntity.propertiesColumns.add(targetProperty); } } } if (!toManyNames.add(toMany.getName().toLowerCase())) { throw new RuntimeException("Duplicate name for " + toMany); } } } private void init3rdPassAdditionalImports() { if (active && !javaPackage.equals(javaPackageDao)) { additionalImportsEntity.add(javaPackageDao + "." + classNameDao); } for (ToOne toOne : toOneRelations) { Entity targetEntity = toOne.getTargetEntity(); checkAdditionalImportsEntityTargetEntity(targetEntity); // For deep loading checkAdditionalImportsDaoTargetEntity(targetEntity); } for (ToManyBase toMany : toManyRelations) { Entity targetEntity = toMany.getTargetEntity(); checkAdditionalImportsEntityTargetEntity(targetEntity); } for (ToManyBase incomingToMany : incomingToManyRelations) { if (incomingToMany instanceof ToManyWithJoinEntity) { final ToManyWithJoinEntity toManyWithJoinEntity = (ToManyWithJoinEntity) incomingToMany; final Entity joinEntity = toManyWithJoinEntity.getJoinEntity(); checkAdditionalImportsDaoTargetEntity(joinEntity); } } for (Property property : properties) { String customType = property.getCustomType(); if (customType != null) { String pack = DaoUtil.getPackageFromFullyQualified(customType); if (pack != null && !pack.equals(javaPackage)) { additionalImportsEntity.add(customType); } if (pack != null && !pack.equals(javaPackageDao)) { additionalImportsDao.add(customType); } } String converter = property.getConverter(); if (converter != null) { String pack = DaoUtil.getPackageFromFullyQualified(converter); if (pack != null && !pack.equals(javaPackageDao)) { additionalImportsDao.add(converter); } } } } private void checkAdditionalImportsEntityTargetEntity(Entity targetEntity) { if (!targetEntity.getJavaPackage().equals(javaPackage)) { additionalImportsEntity.add(targetEntity.getJavaPackage() + "." + targetEntity.getClassName()); } if (!targetEntity.getJavaPackageDao().equals(javaPackage)) { additionalImportsEntity.add(targetEntity.getJavaPackageDao() + "." + targetEntity.getClassNameDao()); } } private void checkAdditionalImportsDaoTargetEntity(Entity targetEntity) { if (!targetEntity.getJavaPackage().equals(javaPackageDao)) { additionalImportsDao.add(targetEntity.getJavaPackage() + "." + targetEntity.getClassName()); } } public void validatePropertyExists(Property property) { if (!properties.contains(property)) { throw new RuntimeException("Property " + property + " does not exist in " + this); } } public List getMultiIndexes() { return multiIndexes; } public boolean isNonDefaultDbName() { return nonDefaultDbName; } @Override public String toString() { return "Entity " + className + " (package: " + javaPackage + ")"; } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/Index.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; @SuppressWarnings("unused") public class Index extends PropertyOrderList { private String name; private boolean unique; private boolean nonDefaultName; public String getName() { return name; } public Index setName(String name) { this.name = name; this.nonDefaultName = name != null; return this; } public Index makeUnique() { unique = true; return this; } public boolean isUnique() { return unique; } public boolean isNonDefaultName() { return nonDefaultName; } /** used internally to know if name is generated by greenDAO */ void setDefaultName(String name) { this.name = name; this.nonDefaultName = false; } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/Property.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; /** Model class for an entity's property: a Java property mapped to a data base column. */ @SuppressWarnings("unused") public class Property { public static class PropertyBuilder { private final Property property; public PropertyBuilder(Schema schema, Entity entity, PropertyType propertyType, String propertyName) { property = new Property(schema, entity, propertyType, propertyName); } @Deprecated /** * @deprecated use dbName */ public PropertyBuilder columnName(String columnName) { return dbName(columnName); } public PropertyBuilder dbName(String dbName) { property.dbName = dbName; property.nonDefaultDbName = dbName != null; return this; } @Deprecated /** * @deprecated use dbType */ public PropertyBuilder columnType(String columnType) { return dbType(columnType); } public PropertyBuilder dbType(String dbType) { property.dbType = dbType; return this; } public PropertyBuilder primaryKey() { property.primaryKey = true; return this; } public PropertyBuilder primaryKeyAsc() { property.primaryKey = true; property.pkAsc = true; return this; } public PropertyBuilder primaryKeyDesc() { property.primaryKey = true; property.pkDesc = true; return this; } public PropertyBuilder autoincrement() { if (!property.primaryKey || property.propertyType != PropertyType.Long) { throw new RuntimeException( "AUTOINCREMENT is only available to primary key properties of type long/Long"); } property.pkAutoincrement = true; return this; } public PropertyBuilder unique() { property.unique = true; return this; } public PropertyBuilder notNull() { property.notNull = true; return this; } public PropertyBuilder nonPrimitiveType() { if (!property.propertyType.isScalar()) { throw new RuntimeException("Type is already non-primitive"); } property.nonPrimitiveType = true; return this; } public PropertyBuilder index() { Index index = new Index(); index.addProperty(property); property.entity.addIndex(index); return this; } public PropertyBuilder indexAsc(String indexNameOrNull, boolean isUnique) { Index index = new Index(); index.addPropertyAsc(property); if (isUnique) { index.makeUnique(); } index.setName(indexNameOrNull); property.entity.addIndex(index); return this; } public PropertyBuilder indexDesc(String indexNameOrNull, boolean isUnique) { Index index = new Index(); index.addPropertyDesc(property); if (isUnique) { index.makeUnique(); } index.setName(indexNameOrNull); property.entity.addIndex(index); return this; } public PropertyBuilder customType(String customType, String converter) { property.customType = customType; property.customTypeClassName = DaoUtil.getClassnameFromFullyQualified(customType); property.converter = converter; property.converterClassName = DaoUtil.getClassnameFromFullyQualified(converter); return this; } public PropertyBuilder codeBeforeField(String code) { property.codeBeforeField = code; return this; } public PropertyBuilder codeBeforeGetter(String code) { property.codeBeforeGetter = code; return this; } public PropertyBuilder codeBeforeSetter(String code) { property.codeBeforeSetter = code; return this; } public PropertyBuilder codeBeforeGetterAndSetter(String code) { property.codeBeforeGetter = code; property.codeBeforeSetter = code; return this; } public PropertyBuilder javaDocField(String javaDoc) { property.javaDocField = checkConvertToJavaDoc(javaDoc); return this; } private String checkConvertToJavaDoc(String javaDoc) { return DaoUtil.checkConvertToJavaDoc(javaDoc, " "); } public PropertyBuilder javaDocGetter(String javaDoc) { property.javaDocGetter = checkConvertToJavaDoc(javaDoc); return this; } public PropertyBuilder javaDocSetter(String javaDoc) { property.javaDocSetter = checkConvertToJavaDoc(javaDoc); return this; } public PropertyBuilder javaDocGetterAndSetter(String javaDoc) { javaDoc = checkConvertToJavaDoc(javaDoc); property.javaDocGetter = javaDoc; property.javaDocSetter = javaDoc; return this; } public Property getProperty() { return property; } } private final Schema schema; private final Entity entity; private PropertyType propertyType; private final String propertyName; private String dbName; private String dbType; private String customType; private String customTypeClassName; private String converter; private String converterClassName; private String codeBeforeField; private String codeBeforeGetter; private String codeBeforeSetter; private String javaDocField; private String javaDocGetter; private String javaDocSetter; private boolean primaryKey; private boolean pkAsc; private boolean pkDesc; private boolean pkAutoincrement; private boolean unique; private boolean notNull; private boolean nonPrimitiveType; /** Initialized in 2nd pass */ private String constraints; private int ordinal; private String javaType; private boolean nonDefaultDbName; /** * Index, which has only this property * Can be added by user via {@link PropertyBuilder} or via {@link Entity#addIndex(Index)} * Initialized in 2nd pass */ private Index index; public Property(Schema schema, Entity entity, PropertyType propertyType, String propertyName) { this.schema = schema; this.entity = entity; this.propertyName = propertyName; this.propertyType = propertyType; } public String getPropertyName() { return propertyName; } public PropertyType getPropertyType() { return propertyType; } public void setPropertyType(PropertyType propertyType) { this.propertyType = propertyType; } public String getDbName() { return dbName; } public boolean isNonDefaultDbName() { return nonDefaultDbName; } public String getDbType() { return dbType; } public boolean isPrimaryKey() { return primaryKey; } public boolean isPkAsc() { return pkAsc; } public boolean isPkDesc() { return pkDesc; } public boolean isAutoincrement() { return pkAutoincrement; } public String getConstraints() { return constraints; } public boolean isUnique() { return unique; } public boolean isNotNull() { return notNull; } public boolean isNonPrimitiveType() { return nonPrimitiveType || !propertyType.isScalar(); } public String getJavaType() { return javaType; } public String getJavaTypeInEntity() { if (customTypeClassName != null) { return customTypeClassName; } else { return javaType; } } public int getOrdinal() { return ordinal; } void setOrdinal(int ordinal) { this.ordinal = ordinal; } public String getCustomType() { return customType; } public String getCustomTypeClassName() { return customTypeClassName; } public String getConverter() { return converter; } public String getConverterClassName() { return converterClassName; } public String getCodeBeforeField() { return codeBeforeField; } public String getCodeBeforeGetter() { return codeBeforeGetter; } public String getCodeBeforeSetter() { return codeBeforeSetter; } public String getJavaDocField() { return javaDocField; } public String getJavaDocGetter() { return javaDocGetter; } public String getJavaDocSetter() { return javaDocSetter; } public String getDatabaseValueExpression() { return getDatabaseValueExpression(propertyName); } public String getDatabaseValueExpressionNotNull() { return getDatabaseValueExpression("entity.get" + DaoUtil.capFirst(propertyName) + "()"); } // Got too messy in template: // <#if property.customType?has_content>${property.propertyName}Converter.convertToDatabaseValue(<#-- // -->entity.get${property.propertyName?cap_first}()<#if property.customType?has_content>)<#if // property.propertyType == "Boolean"> ? 1l: 0l<#if property.propertyType == "Date">.getTime() public String getDatabaseValueExpression(String entityValue) { StringBuilder builder = new StringBuilder(); if (customType != null) { builder.append(propertyName).append("Converter.convertToDatabaseValue("); } builder.append(entityValue); if (customType != null) { builder.append(')'); } if (propertyType == PropertyType.Boolean) { builder.append(" ? 1L: 0L"); } else if (propertyType == PropertyType.Date) { builder.append(".getTime()"); } return builder.toString(); } // Got too messy in template: // <#if property.propertyType == "Byte">(byte) // <#if property.propertyType == "Date">new java.util.Date( // cursor.get${toCursorType[property.propertyType]}(offset + ${property_index}) // <#if property.propertyType == "Boolean"> != 0 // <#if property.propertyType == "Date">) public String getEntityValueExpression(String databaseValue) { StringBuilder builder = new StringBuilder(); if (customType != null) { builder.append(propertyName).append("Converter.convertToEntityProperty("); } if (propertyType == PropertyType.Byte) { builder.append("(byte) "); } else if (propertyType == PropertyType.Date) { builder.append("new java.util.Date("); } builder.append(databaseValue); if (propertyType == PropertyType.Boolean) { builder.append(" != 0"); } else if (propertyType == PropertyType.Date) { builder.append(")"); } if (customType != null) { builder.append(')'); } return builder.toString(); } public Entity getEntity() { return entity; } public Index getIndex() { return index; } public void setIndex(Index index) { this.index = index; } void init2ndPass() { initConstraint(); if (dbType == null) { dbType = schema.mapToDbType(propertyType); } if (dbName == null) { dbName = DaoUtil.dbName(propertyName); nonDefaultDbName = false; } else if (primaryKey && propertyType == PropertyType.Long && dbName.equals("_id")) { nonDefaultDbName = false; } // For backwards compatibility, consider notNull. It should be only dependent on nonPrimitiveType in the future. if (notNull && !nonPrimitiveType) { javaType = schema.mapToJavaTypeNotNull(propertyType); } else { javaType = schema.mapToJavaTypeNullable(propertyType); } } private void initConstraint() { StringBuilder constraintBuilder = new StringBuilder(); if (primaryKey) { constraintBuilder.append("PRIMARY KEY"); if (pkAsc) { constraintBuilder.append(" ASC"); } if (pkDesc) { constraintBuilder.append(" DESC"); } if (pkAutoincrement) { constraintBuilder.append(" AUTOINCREMENT"); } } // Always have String PKs NOT NULL because SQLite is pretty strange in this respect: // One could insert multiple rows with NULL PKs if (notNull || (primaryKey && propertyType == PropertyType.String)) { constraintBuilder.append(" NOT NULL"); } if (unique) { constraintBuilder.append(" UNIQUE"); } String newContraints = constraintBuilder.toString().trim(); if (constraintBuilder.length() > 0) { constraints = newContraints; } } void init3ndPass() { // Nothing to do so far } @Override public String toString() { return "Property " + propertyName + " of " + entity.getClassName(); } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/PropertyOrderList.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import java.util.ArrayList; import java.util.List; public class PropertyOrderList { private List properties; private List propertiesOrder; public PropertyOrderList() { properties = new ArrayList<>(); propertiesOrder = new ArrayList<>(); } public void addProperty(Property property) { properties.add(property); propertiesOrder.add(null); } public void addPropertyAsc(Property property) { properties.add(property); propertiesOrder.add("ASC"); } public void addPropertyDesc(Property property) { properties.add(property); propertiesOrder.add("DESC"); } @SuppressWarnings("unused") public void addOrderRaw(String order) { properties.add(null); propertiesOrder.add(order); } public List getProperties() { return properties; } public List getPropertiesOrder() { return propertiesOrder; } public String getCommaSeparatedString(String tablePrefixOrNull) { StringBuilder builder = new StringBuilder(); int size = properties.size(); for (int i = 0; i < size; i++) { Property property = properties.get(i); String order = propertiesOrder.get(i); if (property != null) { if(tablePrefixOrNull != null) { builder.append(tablePrefixOrNull).append('.'); } builder.append('\'').append(property.getDbName()).append('\'').append(' '); } if (order != null) { builder.append(order); } if (i < size - 1) { builder.append(','); } } return builder.toString(); } public boolean isEmpty() { return properties.isEmpty(); } public String getOrderSpec() { final List properties = getProperties(); final List propertiesOrder = getPropertiesOrder(); final StringBuilder builder = new StringBuilder(); final int size = properties.size(); for (int i = 0; i < size; i++) { final Property property = properties.get(i); final String order = propertiesOrder.get(i); builder.append(property.getPropertyName()); if (order != null) { builder.append(' ').append(order); } if (i < size - 1) { builder.append(", "); } } return builder.toString(); } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/PropertyType.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; /** * Currently available types for properties. * * @author Markus */ public enum PropertyType { Byte(true), Short(true), Int(true), Long(true), Boolean(true), Float(true), Double(true), String(false), ByteArray(false), Date(false); private final boolean scalar; PropertyType(boolean scalar) { this.scalar = scalar; } /** True if the type can be prepresented using a scalar (primitive type). */ public boolean isScalar() { return scalar; } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/Query.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import java.util.ArrayList; import java.util.List; /** NOT IMPLEMENTED YET. Check back later. */ public class Query { @SuppressWarnings("unused") private String name; private List parameters; @SuppressWarnings("unused") private boolean distinct; public Query(String name) { this.name = name; parameters= new ArrayList(); } public QueryParam addEqualsParam(Property column) { return addParam(column, "="); } public QueryParam addParam(Property column, String operator) { QueryParam queryParam = new QueryParam(column, operator); parameters.add(queryParam); return queryParam; } public void distinct() { distinct = true; } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/QueryParam.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; /** NOT IMPLEMENTED YET. Check back later. */ public class QueryParam { private Property column; private String operator; public QueryParam(Property column, String operator) { this.column = column; this.operator = operator; } public Property getColumn() { return column; } public String getOperator() { return operator; } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/Schema.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * The "root" model class to which you can add entities to. * * @see Modelling Entities (Documentation page) */ @SuppressWarnings("unused") public class Schema { public static final String DEFAULT_NAME = "default"; private final int version; private final String defaultJavaPackage; private String defaultJavaPackageDao; private String defaultJavaPackageTest; private final List entities; private Map propertyToDbType; private Map propertyToJavaTypeNotNull; private Map propertyToJavaTypeNullable; private boolean hasKeepSectionsByDefault; private boolean useActiveEntitiesByDefault; private final String name; private final String prefix; public Schema(String name, int version, String defaultJavaPackage) { this.name = name; this.prefix = name.equals(DEFAULT_NAME) ? "" : DaoUtil.capFirst(name); this.version = version; this.defaultJavaPackage = defaultJavaPackage; this.entities = new ArrayList<>(); initTypeMappings(); } public Schema(int version, String defaultJavaPackage) { this(DEFAULT_NAME, version, defaultJavaPackage); } public void enableKeepSectionsByDefault() { hasKeepSectionsByDefault = true; } public void enableActiveEntitiesByDefault() { useActiveEntitiesByDefault = true; } private void initTypeMappings() { propertyToDbType = new HashMap<>(); propertyToDbType.put(PropertyType.Boolean, "INTEGER"); propertyToDbType.put(PropertyType.Byte, "INTEGER"); propertyToDbType.put(PropertyType.Short, "INTEGER"); propertyToDbType.put(PropertyType.Int, "INTEGER"); propertyToDbType.put(PropertyType.Long, "INTEGER"); propertyToDbType.put(PropertyType.Float, "REAL"); propertyToDbType.put(PropertyType.Double, "REAL"); propertyToDbType.put(PropertyType.String, "TEXT"); propertyToDbType.put(PropertyType.ByteArray, "BLOB"); propertyToDbType.put(PropertyType.Date, "INTEGER"); propertyToJavaTypeNotNull = new HashMap<>(); propertyToJavaTypeNotNull.put(PropertyType.Boolean, "boolean"); propertyToJavaTypeNotNull.put(PropertyType.Byte, "byte"); propertyToJavaTypeNotNull.put(PropertyType.Short, "short"); propertyToJavaTypeNotNull.put(PropertyType.Int, "int"); propertyToJavaTypeNotNull.put(PropertyType.Long, "long"); propertyToJavaTypeNotNull.put(PropertyType.Float, "float"); propertyToJavaTypeNotNull.put(PropertyType.Double, "double"); propertyToJavaTypeNotNull.put(PropertyType.String, "String"); propertyToJavaTypeNotNull.put(PropertyType.ByteArray, "byte[]"); propertyToJavaTypeNotNull.put(PropertyType.Date, "java.util.Date"); propertyToJavaTypeNullable = new HashMap<>(); propertyToJavaTypeNullable.put(PropertyType.Boolean, "Boolean"); propertyToJavaTypeNullable.put(PropertyType.Byte, "Byte"); propertyToJavaTypeNullable.put(PropertyType.Short, "Short"); propertyToJavaTypeNullable.put(PropertyType.Int, "Integer"); propertyToJavaTypeNullable.put(PropertyType.Long, "Long"); propertyToJavaTypeNullable.put(PropertyType.Float, "Float"); propertyToJavaTypeNullable.put(PropertyType.Double, "Double"); propertyToJavaTypeNullable.put(PropertyType.String, "String"); propertyToJavaTypeNullable.put(PropertyType.ByteArray, "byte[]"); propertyToJavaTypeNullable.put(PropertyType.Date, "java.util.Date"); } /** * Adds a new entity to the schema. There can be multiple entities per table, but only one may be the primary entity * per table to create table scripts, etc. */ public Entity addEntity(String className) { Entity entity = new Entity(this, className); entities.add(entity); return entity; } /** * Adds a new protocol buffers entity to the schema. There can be multiple entities per table, but only one may be * the primary entity per table to create table scripts, etc. */ public Entity addProtobufEntity(String className) { Entity entity = addEntity(className); entity.useProtobuf(); return entity; } public String mapToDbType(PropertyType propertyType) { return mapType(propertyToDbType, propertyType); } public String mapToJavaTypeNullable(PropertyType propertyType) { return mapType(propertyToJavaTypeNullable, propertyType); } public String mapToJavaTypeNotNull(PropertyType propertyType) { return mapType(propertyToJavaTypeNotNull, propertyType); } private String mapType(Map map, PropertyType propertyType) { String dbType = map.get(propertyType); if (dbType == null) { throw new IllegalStateException("No mapping for " + propertyType); } return dbType; } public int getVersion() { return version; } public String getDefaultJavaPackage() { return defaultJavaPackage; } public String getDefaultJavaPackageDao() { return defaultJavaPackageDao; } public void setDefaultJavaPackageDao(String defaultJavaPackageDao) { this.defaultJavaPackageDao = defaultJavaPackageDao; } public String getDefaultJavaPackageTest() { return defaultJavaPackageTest; } public void setDefaultJavaPackageTest(String defaultJavaPackageTest) { this.defaultJavaPackageTest = defaultJavaPackageTest; } public List getEntities() { return entities; } public boolean isHasKeepSectionsByDefault() { return hasKeepSectionsByDefault; } public boolean isUseActiveEntitiesByDefault() { return useActiveEntitiesByDefault; } public String getName() { return name; } public String getPrefix() { return prefix; } void init2ndPass() { if (defaultJavaPackageDao == null) { defaultJavaPackageDao = defaultJavaPackage; } if (defaultJavaPackageTest == null) { defaultJavaPackageTest = defaultJavaPackageDao; } for (Entity entity : entities) { entity.init2ndPass(); } } void init3rdPass() { for (Entity entity : entities) { entity.init3rdPass(); } } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/ToMany.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import java.util.List; /** To-many relationship from a source entity to many target entities. */ @SuppressWarnings("unused") public class ToMany extends ToManyBase { private Property[] sourceProperties; private final Property[] targetProperties; public ToMany(Schema schema, Entity sourceEntity, Property[] sourceProperties, Entity targetEntity, Property[] targetProperties) { super(schema, sourceEntity, targetEntity); this.sourceProperties = sourceProperties; this.targetProperties = targetProperties; } public Property[] getSourceProperties() { return sourceProperties; } public void setSourceProperties(Property[] sourceProperties) { this.sourceProperties = sourceProperties; } public Property[] getTargetProperties() { return targetProperties; } void init2ndPass() { super.init2ndPass(); if (sourceProperties == null) { List pks = sourceEntity.getPropertiesPk(); if (pks.isEmpty()) { throw new RuntimeException("Source entity has no primary key, but we need it for " + this); } sourceProperties = new Property[pks.size()]; sourceProperties = pks.toArray(sourceProperties); } int count = sourceProperties.length; if (count != targetProperties.length) { throw new RuntimeException("Source properties do not match target properties: " + this); } for (int i = 0; i < count; i++) { Property sourceProperty = sourceProperties[i]; Property targetProperty = targetProperties[i]; PropertyType sourceType = sourceProperty.getPropertyType(); PropertyType targetType = targetProperty.getPropertyType(); if (sourceType == null || targetType == null) { throw new RuntimeException("Property type uninitialized"); } if (sourceType != targetType) { System.err.println("Warning to-one property type does not match target key type: " + this); } } } void init3rdPass() { super.init3rdPass(); } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/ToManyBase.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; /** Base class for to-many relationship from source entities to target entities. */ @SuppressWarnings("unused") public abstract class ToManyBase { @SuppressWarnings("unused") private final Schema schema; private String name; protected final Entity sourceEntity; protected final Entity targetEntity; private final PropertyOrderList propertyOrderList; public ToManyBase(Schema schema, Entity sourceEntity, Entity targetEntity) { this.schema = schema; this.sourceEntity = sourceEntity; this.targetEntity = targetEntity; propertyOrderList = new PropertyOrderList(); } public Entity getSourceEntity() { return sourceEntity; } public Entity getTargetEntity() { return targetEntity; } public String getName() { return name; } /** * Sets the name of the relation, which is used as the property name in the entity (the source entity owning the * to-many relationship). */ public void setName(String name) { this.name = name; } /** Property of target entity used for ascending order. */ public void orderAsc(Property... properties) { for (Property property : properties) { targetEntity.validatePropertyExists(property); propertyOrderList.addPropertyAsc(property); } } /** Property of target entity used for descending order. */ public void orderDesc(Property... properties) { for (Property property : properties) { targetEntity.validatePropertyExists(property); propertyOrderList.addPropertyDesc(property); } } public String getOrder() { if (propertyOrderList.isEmpty()) { return null; } else { // Table prefix must match default of QueryBuilder in DaoCore return propertyOrderList.getCommaSeparatedString("T"); } } /** order spec to be used in generated @OrderBy annotation */ public String getOrderSpec() { if (propertyOrderList.isEmpty()) { return null; } else { return propertyOrderList.getOrderSpec(); } } void init2ndPass() { if (name == null) { char[] nameCharArray = targetEntity.getClassName().toCharArray(); nameCharArray[0] = Character.toLowerCase(nameCharArray[0]); name = new String(nameCharArray) + "List"; } } void init3rdPass() { } @Override public String toString() { String sourceName = sourceEntity != null ? sourceEntity.getClassName() : null; String targetName = targetEntity != null ? targetEntity.getClassName() : null; return "ToMany '" + name + "' from " + sourceName + " to " + targetName; } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/ToManyWithJoinEntity.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import java.util.List; /** To-many relationship to many target entities using a join entity (aka JOIN table). */ @SuppressWarnings("unused") public class ToManyWithJoinEntity extends ToManyBase { private final Entity joinEntity; private final Property sourceProperty; private final Property targetProperty; public ToManyWithJoinEntity(Schema schema, Entity sourceEntity, Entity targetEntity, Entity joinEntity, Property sourceProperty, Property targetProperty) { super(schema, sourceEntity, targetEntity); this.joinEntity = joinEntity; this.sourceProperty = sourceProperty; this.targetProperty = targetProperty; } public Entity getJoinEntity() { return joinEntity; } public Property getSourceProperty() { return sourceProperty; } public Property getTargetProperty() { return targetProperty; } void init3rdPass() { super.init3rdPass(); List pks = sourceEntity.getPropertiesPk(); if (pks.isEmpty()) { throw new RuntimeException("Source entity has no primary key, but we need it for " + this); } List pks2 = targetEntity.getPropertiesPk(); if (pks2.isEmpty()) { throw new RuntimeException("Target entity has no primary key, but we need it for " + this); } } } ================================================ FILE: DaoGenerator/src/org/greenrobot/greendao/generator/ToOne.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; /** To-one relationship from a source entity to one (or zero) target entity. */ @SuppressWarnings("unused") public class ToOne { private final Schema schema; private final Entity sourceEntity; private final Entity targetEntity; private final Property[] fkProperties; private final String[] resolvedKeyJavaType; private final boolean[] resolvedKeyUseEquals; private String name; private final boolean useFkProperty; public ToOne(Schema schema, Entity sourceEntity, Entity targetEntity, Property[] fkProperties, boolean useFkProperty) { this.schema = schema; this.sourceEntity = sourceEntity; this.targetEntity = targetEntity; this.fkProperties = fkProperties; this.useFkProperty = useFkProperty; resolvedKeyJavaType = new String[fkProperties.length]; resolvedKeyUseEquals = new boolean[fkProperties.length]; } public Entity getSourceEntity() { return sourceEntity; } public Entity getTargetEntity() { return targetEntity; } public Property[] getFkProperties() { return fkProperties; } public String[] getResolvedKeyJavaType() { return resolvedKeyJavaType; } public boolean[] getResolvedKeyUseEquals() { return resolvedKeyUseEquals; } public String getName() { return name; } /** * Sets the name of the relation, which is used as the property name in the entity (the source entity owning the * to-many relationship). */ public void setName(String name) { this.name = name; } public boolean isUseFkProperty() { return useFkProperty; } void init2ndPass() { if (name == null) { char[] nameCharArray = targetEntity.getClassName().toCharArray(); nameCharArray[0] = Character.toLowerCase(nameCharArray[0]); name = new String(nameCharArray); } } /** Constructs fkColumns. Depends on 2nd pass of target key properties. */ void init3ndPass() { Property targetPkProperty = targetEntity.getPkProperty(); if (fkProperties.length != 1 || targetPkProperty == null) { throw new RuntimeException("Currently only single FK columns are supported: " + this); } Property property = fkProperties[0]; PropertyType propertyType = property.getPropertyType(); if (propertyType == null) { propertyType = targetPkProperty.getPropertyType(); property.setPropertyType(propertyType); // Property is not a regular property with primitive getters/setters, so let it catch up property.init2ndPass(); property.init3ndPass(); } else if (propertyType != targetPkProperty.getPropertyType()) { System.err.println("Warning to-one property type does not match target key type: " + this); } resolvedKeyJavaType[0] = schema.mapToJavaTypeNullable(propertyType); resolvedKeyUseEquals[0] = checkUseEquals(propertyType); } protected boolean checkUseEquals(PropertyType propertyType) { boolean useEquals; switch (propertyType) { case Byte: case Short: case Int: case Long: case Boolean: case Float: useEquals = true; break; default: useEquals = false; break; } return useEquals; } @Override public String toString() { String sourceName = sourceEntity != null ? sourceEntity.getClassName() : null; String targetName = targetEntity != null ? targetEntity.getClassName() : null; return "ToOne '" + name + "' from " + sourceName + " to " + targetName; } } ================================================ FILE: DaoGenerator/src-template/content-provider.ftl ================================================ <#-- @ftlvariable name="entity" type="org.greenrobot.greendao.generator.Entity" --> <#-- @ftlvariable name="contentProvider" type="org.greenrobot.greendao.generator.ContentProvider" --> <#-- @ftlvariable name="schema" type="org.greenrobot.greendao.generator.Schema" --> package ${contentProvider.javaPackage}; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import org.greenrobot.greendao.DaoLog; import org.greenrobot.greendao.database.StandardDatabase; import org.greenrobot.greendao.database.Database; import ${schema.defaultJavaPackageDao}.${schema.prefix}DaoSession; import ${entity.javaPackageDao}.${entity.classNameDao}; /* Copy this code snippet into your AndroidManifest.xml inside the element: */ public class ${contentProvider.className} extends ContentProvider { public static final String AUTHORITY = "${contentProvider.authority}"; public static final String BASE_PATH = "${contentProvider.basePath}"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH); public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + BASE_PATH; public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + BASE_PATH; private static final String TABLENAME = ${entity.classNameDao}.TABLENAME; private static final String PK = ${entity.classNameDao}.Properties.${entity.pkProperty.propertyName?cap_first}.columnName; <#assign counter = 0> private static final int ${entity.className?upper_case}_DIR = ${counter}; private static final int ${entity.className?upper_case}_ID = ${counter+1}; private static final UriMatcher sURIMatcher; static { sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); sURIMatcher.addURI(AUTHORITY, BASE_PATH, ${entity.className?upper_case}_DIR); sURIMatcher.addURI(AUTHORITY, BASE_PATH + "/#", ${entity.className?upper_case}_ID); } /** * This must be set from outside, it's recommended to do this inside your Application object. * Subject to change (static isn't nice). */ public static ${schema.prefix}DaoSession daoSession; @Override public boolean onCreate() { // if(daoSession == null) { // throw new IllegalStateException("${schema.prefix}DaoSession must be set before content provider is created"); // } DaoLog.d("Content Provider started: " + CONTENT_URI); return true; } protected Database getDatabase() { if(daoSession == null) { throw new IllegalStateException("${schema.prefix}DaoSession must be set during content provider is active"); } return daoSession.getDatabase(); } <#-- ########################################## ########## Insert ############## ########################################## --> @Override public Uri insert(Uri uri, ContentValues values) { <#if contentProvider.isReadOnly()> throw new UnsupportedOperationException("This content provider is readonly"); <#else> int uriType = sURIMatcher.match(uri); long id = 0; String path = ""; switch (uriType) { case ${entity.className?upper_case}_DIR: id = getDatabase().insert(TABLENAME, null, values); path = BASE_PATH + "/" + id; break; default: throw new IllegalArgumentException("Unknown URI: " + uri); } getContext().getContentResolver().notifyChange(uri, null); return Uri.parse(path); } <#-- ########################################## ########## Delete ############## ########################################## --> @Override public int delete(Uri uri, String selection, String[] selectionArgs) { <#if contentProvider.isReadOnly()> throw new UnsupportedOperationException("This content provider is readonly"); <#else> int uriType = sURIMatcher.match(uri); Database db = getDatabase(); int rowsDeleted = 0; String id; switch (uriType) { case ${entity.className?upper_case}_DIR: rowsDeleted = db.delete(TABLENAME, selection, selectionArgs); break; case ${entity.className?upper_case}_ID: id = uri.getLastPathSegment(); if (TextUtils.isEmpty(selection)) { rowsDeleted = db.delete(TABLENAME, PK + "=" + id, null); } else { rowsDeleted = db.delete(TABLENAME, PK + "=" + id + " and " + selection, selectionArgs); } break; default: throw new IllegalArgumentException("Unknown URI: " + uri); } getContext().getContentResolver().notifyChange(uri, null); return rowsDeleted; } <#-- ########################################## ########## Update ############## ########################################## --> @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { <#if contentProvider.isReadOnly()> throw new UnsupportedOperationException("This content provider is readonly"); <#else> int uriType = sURIMatcher.match(uri); Database db = getDatabase(); int rowsUpdated = 0; String id; switch (uriType) { case ${entity.className?upper_case}_DIR: rowsUpdated = db.update(TABLENAME, values, selection, selectionArgs); break; case ${entity.className?upper_case}_ID: id = uri.getLastPathSegment(); if (TextUtils.isEmpty(selection)) { rowsUpdated = db.update(TABLENAME, values, PK + "=" + id, null); } else { rowsUpdated = db.update(TABLENAME, values, PK + "=" + id + " and " + selection, selectionArgs); } break; default: throw new IllegalArgumentException("Unknown URI: " + uri); } getContext().getContentResolver().notifyChange(uri, null); return rowsUpdated; } <#-- ########################################## ########## Query ############## ########################################## --> @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); int uriType = sURIMatcher.match(uri); switch (uriType) { case ${entity.className?upper_case}_DIR: queryBuilder.setTables(TABLENAME); break; case ${entity.className?upper_case}_ID: queryBuilder.setTables(TABLENAME); queryBuilder.appendWhere(PK + "=" + uri.getLastPathSegment()); break; default: throw new IllegalArgumentException("Unknown URI: " + uri); } Database db = getDatabase(); Cursor cursor = queryBuilder.query(((StandardDatabase) db).getSQLiteDatabase(), projection, selection, selectionArgs, null, null, sortOrder); cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; } <#-- ########################################## ########## GetType ############## ########################################## --> @Override public final String getType(Uri uri) { switch (sURIMatcher.match(uri)) { case ${entity.className?upper_case}_DIR: return CONTENT_TYPE; case ${entity.className?upper_case}_ID: return CONTENT_ITEM_TYPE; default : throw new IllegalArgumentException("Unsupported URI: " + uri); } } } ================================================ FILE: DaoGenerator/src-template/dao-deep.ftl ================================================ <#-- Copyright (C) 2011-2015 Markus Junginger, greenrobot (http://greenrobot.de) This file is part of greenDAO Generator. greenDAO Generator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. greenDAO Generator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with greenDAO Generator. If not, see . --> <#if entity.toOneRelations?has_content> private String selectDeep; protected String getSelectDeep() { if (selectDeep == null) { StringBuilder builder = new StringBuilder("SELECT "); SqlUtils.appendColumns(builder, "T", getAllColumns()); builder.append(','); <#list entity.toOneRelations as toOne> SqlUtils.appendColumns(builder, "T${toOne_index}", daoSession.get${toOne.targetEntity.classNameDao}().getAllColumns()); <#if toOne_has_next> builder.append(','); builder.append(" FROM ${entity.dbName} T"); <#list entity.toOneRelations as toOne> builder.append(" LEFT JOIN ${toOne.targetEntity.dbName} T${toOne_index}<#-- --> ON T.\"${toOne.fkProperties[0].dbName}\"=T${toOne_index}.\"${toOne.targetEntity.pkProperty.dbName}\""); builder.append(' '); selectDeep = builder.toString(); } return selectDeep; } protected ${entity.className} loadCurrentDeep(Cursor cursor, boolean lock) { ${entity.className} entity = loadCurrent(cursor, 0, lock); int offset = getAllColumns().length; <#list entity.toOneRelations as toOne> ${toOne.targetEntity.className} ${toOne.name} = loadCurrentOther(daoSession.get${toOne.targetEntity.classNameDao}(), cursor, offset); <#if toOne.fkProperties[0].notNull> if(${toOne.name} != null) { entity.set${toOne.name?cap_first}(${toOne.name}); <#if toOne.fkProperties[0].notNull> } <#if toOne_has_next> offset += daoSession.get${toOne.targetEntity.classNameDao}().getAllColumns().length; return entity; } public ${entity.className} loadDeep(Long key) { assertSinglePk(); if (key == null) { return null; } StringBuilder builder = new StringBuilder(getSelectDeep()); builder.append("WHERE "); SqlUtils.appendColumnsEqValue(builder, "T", getPkColumns()); String sql = builder.toString(); String[] keyArray = new String[] { key.toString() }; Cursor cursor = db.rawQuery(sql, keyArray); try { boolean available = cursor.moveToFirst(); if (!available) { return null; } else if (!cursor.isLast()) { throw new IllegalStateException("Expected unique result, but count was " + cursor.getCount()); } return loadCurrentDeep(cursor, true); } finally { cursor.close(); } } /** Reads all available rows from the given cursor and returns a list of new ImageTO objects. */ public List<${entity.className}> loadAllDeepFromCursor(Cursor cursor) { int count = cursor.getCount(); List<${entity.className}> list = new ArrayList<${entity.className}>(count); if (cursor.moveToFirst()) { if (identityScope != null) { identityScope.lock(); identityScope.reserveRoom(count); } try { do { list.add(loadCurrentDeep(cursor, false)); } while (cursor.moveToNext()); } finally { if (identityScope != null) { identityScope.unlock(); } } } return list; } protected List<${entity.className}> loadDeepAllAndCloseCursor(Cursor cursor) { try { return loadAllDeepFromCursor(cursor); } finally { cursor.close(); } } /** A raw-style query where you can pass any WHERE clause and arguments. */ public List<${entity.className}> queryDeep(String where, String... selectionArg) { Cursor cursor = db.rawQuery(getSelectDeep() + where, selectionArg); return loadDeepAllAndCloseCursor(cursor); } ================================================ FILE: DaoGenerator/src-template/dao-master.ftl ================================================ <#-- Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) This file is part of greenDAO Generator. greenDAO Generator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. greenDAO Generator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with greenDAO Generator. If not, see . --> <#-- @ftlvariable name="schema" type="org.greenrobot.greendao.generator.Schema" --> package ${schema.defaultJavaPackageDao}; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.util.Log; import org.greenrobot.greendao.AbstractDaoMaster; import org.greenrobot.greendao.database.StandardDatabase; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.database.DatabaseOpenHelper; import org.greenrobot.greendao.identityscope.IdentityScopeType; <#list schema.entities as entity> <#if schema.defaultJavaPackageDao != entity.javaPackageDao> import ${entity.javaPackageDao}.${entity.classNameDao}; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * Master of DAO (schema version ${schema.version?c}): knows all DAOs. */ public class ${schema.prefix}DaoMaster extends AbstractDaoMaster { public static final int SCHEMA_VERSION = ${schema.version?c}; /** Creates underlying database table using DAOs. */ public static void createAllTables(Database db, boolean ifNotExists) { <#list schema.entities as entity> <#if !entity.skipCreationInDb> ${entity.classNameDao}.createTable(db, ifNotExists); } /** Drops underlying database table using DAOs. */ public static void dropAllTables(Database db, boolean ifExists) { <#list schema.entities as entity> <#if !entity.skipCreationInDb> ${entity.classNameDao}.dropTable(db, ifExists); } /** * WARNING: Drops all table on Upgrade! Use only during development. * Convenience method using a {@link DevOpenHelper}. */ public static ${schema.prefix}DaoSession newDevSession(Context context, String name) { Database db = new DevOpenHelper(context, name).getWritableDb(); ${schema.prefix}DaoMaster daoMaster = new ${schema.prefix}DaoMaster(db); return daoMaster.newSession(); } public ${schema.prefix}DaoMaster(SQLiteDatabase db) { this(new StandardDatabase(db)); } public ${schema.prefix}DaoMaster(Database db) { super(db, SCHEMA_VERSION); <#list schema.entities as entity> registerDaoClass(${entity.classNameDao}.class); } public ${schema.prefix}DaoSession newSession() { return new ${schema.prefix}DaoSession(db, IdentityScopeType.Session, daoConfigMap); } public ${schema.prefix}DaoSession newSession(IdentityScopeType type) { return new ${schema.prefix}DaoSession(db, type, daoConfigMap); } /** * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} - */ public static abstract class OpenHelper extends DatabaseOpenHelper { public OpenHelper(Context context, String name) { super(context, name, SCHEMA_VERSION); } public OpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory, SCHEMA_VERSION); } @Override public void onCreate(Database db) { Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION); createAllTables(db, false); } } /** WARNING: Drops all table on Upgrade! Use only during development. */ public static class DevOpenHelper extends OpenHelper { public DevOpenHelper(Context context, String name) { super(context, name); } public DevOpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory); } @Override public void onUpgrade(Database db, int oldVersion, int newVersion) { Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); dropAllTables(db, true); onCreate(db); } } } ================================================ FILE: DaoGenerator/src-template/dao-session.ftl ================================================ <#-- Copyright (C) 2011 Markus Junginger, greenrobot (http://greenrobot.de) This file is part of greenDAO Generator. greenDAO Generator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. greenDAO Generator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with greenDAO Generator. If not, see . --> package ${schema.defaultJavaPackageDao}; import java.util.Map; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.AbstractDaoSession; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.identityscope.IdentityScopeType; import org.greenrobot.greendao.internal.DaoConfig; <#list schema.entities as entity> import ${entity.javaPackage}.${entity.className}; <#list schema.entities as entity> import ${entity.javaPackageDao}.${entity.classNameDao}; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * {@inheritDoc} * * @see org.greenrobot.greendao.AbstractDaoSession */ public class ${schema.prefix}DaoSession extends AbstractDaoSession { <#list schema.entities as entity> private final DaoConfig ${entity.classNameDao?uncap_first}Config; <#list schema.entities as entity> private final ${entity.classNameDao} ${entity.classNameDao?uncap_first}; public ${schema.prefix}DaoSession(Database db, IdentityScopeType type, Map>, DaoConfig> daoConfigMap) { super(db); <#list schema.entities as entity> ${entity.classNameDao?uncap_first}Config = daoConfigMap.get(${entity.classNameDao}.class).clone(); ${entity.classNameDao?uncap_first}Config.initIdentityScope(type); <#list schema.entities as entity> ${entity.classNameDao?uncap_first} = new ${entity.classNameDao}<#-- -->(${entity.classNameDao?uncap_first}Config, this); <#list schema.entities as entity> registerDao(${entity.className}.class, ${entity.classNameDao?uncap_first}); } public void clear() { <#list schema.entities as entity> ${entity.classNameDao?uncap_first}Config.clearIdentityScope(); } <#list schema.entities as entity> public ${entity.classNameDao} get${entity.classNameDao?cap_first}() { return ${entity.classNameDao?uncap_first}; } } ================================================ FILE: DaoGenerator/src-template/dao-unit-test.ftl ================================================ <#-- Copyright (C) 2011 Markus Junginger, greenrobot (http://greenrobot.de) This file is part of greenDAO Generator. greenDAO Generator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. greenDAO Generator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with greenDAO Generator. If not, see . --> package ${entity.javaPackageTest}; <#assign isStringPK = entity.pkProperty?? && entity.pkProperty.propertyType == "String" /> <#if isStringPK> import org.greenrobot.greendao.test.AbstractDaoTestStringPk; <#else> import org.greenrobot.greendao.test.AbstractDaoTestLongPk; import ${entity.javaPackage}.${entity.className}; import ${entity.javaPackageDao}.${entity.classNameDao}; public class ${entity.classNameTest} extends <#if isStringPK>AbstractDaoTestStringPk<${entity.classNameDao}, ${entity.className}><#else>AbstractDaoTestLongPk<${entity.classNameDao}, ${entity.className}> { public ${entity.classNameTest}() { super(${entity.classNameDao}.class); } @Override protected ${entity.className} createEntity(<#if isStringPK>String<#else>Long key) { ${entity.className} entity = new ${entity.className}(); <#if entity.pkProperty??> entity.set${entity.pkProperty.propertyName?cap_first}(key); <#list entity.properties as property> <#if property.notNull> entity.set${property.propertyName?cap_first}(); return entity; } } ================================================ FILE: DaoGenerator/src-template/dao.ftl ================================================ <#-- Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) This file is part of greenDAO Generator. greenDAO Generator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. greenDAO Generator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with greenDAO Generator. If not, see . --> <#-- @ftlvariable name="entity" type="org.greenrobot.greendao.generator.Entity" --> <#-- @ftlvariable name="schema" type="org.greenrobot.greendao.generator.Schema" --> <#assign toBindType = {"Boolean":"Long", "Byte":"Long", "Short":"Long", "Int":"Long", "Long":"Long", "Float":"Double", "Double":"Double", "String":"String", "ByteArray":"Blob", "Date": "Long" } /> <#assign toCursorType = {"Boolean":"Short", "Byte":"Short", "Short":"Short", "Int":"Int", "Long":"Long", "Float":"Float", "Double":"Double", "String":"String", "ByteArray":"Blob", "Date": "Long" } /> package ${entity.javaPackageDao}; <#if entity.toOneRelations?has_content || entity.incomingToManyRelations?has_content> import java.util.List; <#if entity.toOneRelations?has_content> import java.util.ArrayList; import android.database.Cursor; import android.database.sqlite.SQLiteStatement; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.Property; <#if entity.toOneRelations?has_content> import org.greenrobot.greendao.internal.SqlUtils; import org.greenrobot.greendao.internal.DaoConfig; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.database.DatabaseStatement; <#if entity.incomingToManyRelations?has_content> import org.greenrobot.greendao.query.Query; import org.greenrobot.greendao.query.QueryBuilder; <#if entity.javaPackageDao != schema.defaultJavaPackageDao> import ${schema.defaultJavaPackageDao}.${schema.prefix}DaoSession; <#if entity.additionalImportsDao?has_content> <#list entity.additionalImportsDao as additionalImport> import ${additionalImport}; <#if entity.javaPackageDao != entity.javaPackage> import ${entity.javaPackage}.${entity.className}; <#if entity.protobuf> import ${entity.javaPackage}.${entity.className}.Builder; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * DAO for table "${entity.dbName}". */ public class ${entity.classNameDao} extends AbstractDao<${entity.className}, ${entity.pkType}> { public static final String TABLENAME = "${entity.dbName}"; /** * Properties of entity ${entity.className}.
* Can be used for QueryBuilder and for referencing column names. */ public static class Properties { <#list entity.propertiesColumns as property> public final static Property ${property.propertyName?cap_first} = new Property(${property_index}, ${property.javaType}.class, "${property.propertyName}", ${property.primaryKey?string}, "${property.dbName}"); } <#if entity.active> private ${schema.prefix}DaoSession daoSession; <#list entity.properties as property><#if property.customType?has_content><#-- --> private final ${property.converterClassName} ${property.propertyName}Converter = new ${property.converterClassName}(); <#list entity.incomingToManyRelations as toMany> private Query<${toMany.targetEntity.className}> ${toMany.sourceEntity.className?uncap_first}_${toMany.name?cap_first}Query; public ${entity.classNameDao}(DaoConfig config) { super(config); } public ${entity.classNameDao}(DaoConfig config, ${schema.prefix}DaoSession daoSession) { super(config, daoSession); <#if entity.active> this.daoSession = daoSession; } <#if !entity.skipCreationInDb> /** Creates the underlying database table. */ public static void createTable(Database db, boolean ifNotExists) { String constraint = ifNotExists? "IF NOT EXISTS ": ""; db.execSQL("CREATE TABLE " + constraint + "\"${entity.dbName}\" (" + // <#list entity.propertiesColumns as property> "\"${property.dbName}\" ${property.dbType}<#if property.constraints??> ${property.constraints} <#if property_has_next>," +<#else>);"); // ${property_index}: ${property.propertyName} <#if entity.indexes?has_content > // Add Indexes <#list entity.indexes as index> db.execSQL("CREATE <#if index.unique>UNIQUE INDEX " + constraint + "${index.name} ON \"${entity.dbName}\"" + " (<#list index.properties as property>\"${property.dbName}\"<#if (index.propertiesOrder[property_index])??> ${index.propertiesOrder[property_index]}<#sep>,);"); } /** Drops the underlying database table. */ public static void dropTable(Database db, boolean ifExists) { String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"${entity.dbName}\""; db.execSQL(sql); } <#assign stmtTypes = ["DatabaseStatement", "SQLiteStatement"] /> <#list stmtTypes as stmtType> @Override protected final void bindValues(${stmtType} stmt, ${entity.className} entity) { stmt.clearBindings(); <#list entity.properties as property> <#if property.notNull || entity.protobuf> <#if entity.protobuf> if(entity.has${property.propertyName?cap_first}()) { stmt.bind${toBindType[property.propertyType]}(${property_index + 1}, ${property.databaseValueExpressionNotNull}); <#if entity.protobuf> } <#else> <#-- nullable, non-protobuff --> ${property.javaTypeInEntity} ${property.propertyName} = entity.get${property.propertyName?cap_first}(); if (${property.propertyName} != null) { stmt.bind${toBindType[property.propertyType]}(${property_index + 1}, ${property.databaseValueExpression}); } <#list entity.toOneRelations as toOne> <#if !toOne.fkProperties?has_content> ${toOne.targetEntity.className} ${toOne.name} = entity.peak${toOne.name?cap_first}(); if(${toOne.name} != null) { ${toOne.targetEntity.pkProperty.javaType} ${toOne.name}__targetKey = ${toOne.name}.get${toOne.targetEntity.pkProperty.propertyName?cap_first}(); <#if !toOne.targetEntity.pkProperty.notNull> if(${toOne.name}__targetKey != null) { // TODO bind ${toOne.name}__targetKey } <#else> // TODO bind ${toOne.name}__targetKey } } <#if entity.active && !entity.protobuf> @Override protected final void attachEntity(${entity.className} entity) { super.attachEntity(entity); entity.__setDaoSession(daoSession); } @Override public ${entity.pkType} readKey(Cursor cursor, int offset) { <#if entity.pkProperty??> return <#if !entity.pkProperty.notNull>cursor.isNull(offset + ${entity.pkProperty.ordinal}) ? null : <#if entity.pkProperty.propertyType == "Byte">(byte) <#if entity.pkProperty.propertyType == "Date">new java.util.Date(cursor.get${toCursorType[entity.pkProperty.propertyType]}(offset + ${entity.pkProperty.ordinal})<#if entity.pkProperty.propertyType == "Boolean"> != 0<#if entity.pkProperty.propertyType == "Date">); <#else> return null; } @Override public ${entity.className} readEntity(Cursor cursor, int offset) { <#if entity.protobuf> Builder builder = ${entity.className}.newBuilder(); <#list entity.properties as property> <#if !property.notNull> if (!cursor.isNull(offset + ${property_index})) { builder.set${property.propertyName?cap_first}(cursor.get${toCursorType[property.propertyType]}(offset + ${property_index})); <#if !property.notNull> } return builder.build(); <#elseif entity.constructors> <#-- ############################## readEntity non-protobuff, constructor ############################## --> ${entity.className} entity = new ${entity.className}( // <#list entity.properties as property> <#if !property.notNull>cursor.isNull(offset + ${property_index}) ? null : <#-- -->${property.getEntityValueExpression("cursor.get${toCursorType[property.propertyType]}(offset + ${property_index})")}<#-- --><#if property_has_next>, // ${property.propertyName} ); return entity; <#else> <#-- ############################## readEntity non-protobuff, setters ############################## --> ${entity.className} entity = new ${entity.className}(); readEntity(cursor, entity, offset); return entity; } @Override public void readEntity(Cursor cursor, ${entity.className} entity, int offset) { <#if entity.protobuf> throw new UnsupportedOperationException("Protobuf objects cannot be modified"); <#else> <#list entity.properties as property> entity.set${property.propertyName?cap_first}(<#if !property.notNull>cursor.isNull(offset + ${property_index}) ? null : <#-- -->${property.getEntityValueExpression("cursor.get${toCursorType[property.propertyType]}(offset + ${property_index})")}); } @Override protected final ${entity.pkType} updateKeyAfterInsert(${entity.className} entity, long rowId) { <#if entity.pkProperty??> <#if entity.pkProperty.propertyType == "Long"> <#if !entity.protobuf> entity.set${entity.pkProperty.propertyName?cap_first}(rowId); return rowId; <#else> return entity.get${entity.pkProperty.propertyName?cap_first}(); <#else> // Unsupported or missing PK type return null; } @Override public ${entity.pkType} getKey(${entity.className} entity) { <#if entity.pkProperty??> if(entity != null) { return entity.get${entity.pkProperty.propertyName?cap_first}(); } else { return null; } <#else> return null; } @Override public boolean hasKey(${entity.className} entity) { <#if entity.pkProperty??> <#if entity.pkProperty.notNull> throw new UnsupportedOperationException("Unsupported for entities with a non-null key"); <#else> <#if entity.protobuf> return entity.has${entity.pkProperty.propertyName?cap_first}(); <#else> return entity.get${entity.pkProperty.propertyName?cap_first}() != null; <#else> // TODO return false; } @Override protected final boolean isEntityUpdateable() { return ${(!entity.protobuf)?string}; } <#list entity.incomingToManyRelations as toMany> /** Internal query to resolve the "${toMany.name}" to-many relationship of ${toMany.sourceEntity.className}. */ public List<${toMany.targetEntity.className}> _query${toMany.sourceEntity.className?cap_first}_${toMany.name?cap_first}(<#-- --><#if toMany.targetProperties??><#list toMany.targetProperties as property><#-- -->${property.javaType} ${property.propertyName}<#if property_has_next>, <#else><#-- -->${toMany.sourceProperty.javaType} ${toMany.sourceProperty.propertyName}) { synchronized (this) { if (${toMany.sourceEntity.className?uncap_first}_${toMany.name?cap_first}Query == null) { QueryBuilder<${toMany.targetEntity.className}> queryBuilder = queryBuilder(); <#if toMany.targetProperties??> <#list toMany.targetProperties as property> queryBuilder.where(Properties.${property.propertyName?cap_first}.eq(null)); <#else> queryBuilder.join(${toMany.joinEntity.className}.class, ${toMany.joinEntity.classNameDao}.Properties.${toMany.targetProperty.propertyName?cap_first}) .where(${toMany.joinEntity.classNameDao}.Properties.${toMany.sourceProperty.propertyName?cap_first}.eq(${toMany.sourceProperty.propertyName})); <#if toMany.order?has_content> queryBuilder.orderRaw("${toMany.order}"); ${toMany.sourceEntity.className?uncap_first}_${toMany.name?cap_first}Query = queryBuilder.build(); } } Query<${toMany.targetEntity.className}> query = ${toMany.sourceEntity.className?uncap_first}_${toMany.name?cap_first}Query.forCurrentThread(); <#if toMany.targetProperties??> <#list toMany.targetProperties as property> query.setParameter(${property_index}, ${property.propertyName}); <#else> query.setParameter(0, ${toMany.sourceProperty.propertyName}); return query.list(); } <#if entity.toOneRelations?has_content> <#include "dao-deep.ftl"> } ================================================ FILE: DaoGenerator/src-template/entity.ftl ================================================ <#-- Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) This file is part of greenDAO Generator. greenDAO Generator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. greenDAO Generator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with greenDAO Generator. If not, see . --> <#-- @ftlvariable name="entity" type="org.greenrobot.greendao.generator.Entity" --> <#-- @ftlvariable name="schema" type="org.greenrobot.greendao.generator.Schema" --> <#assign toBindType = {"Boolean":"Long", "Byte":"Long", "Short":"Long", "Int":"Long", "Long":"Long", "Float":"Double", "Double":"Double", "String":"String", "ByteArray":"Blob" }/> <#assign toCursorType = {"Boolean":"Short", "Byte":"Short", "Short":"Short", "Int":"Int", "Long":"Long", "Float":"Float", "Double":"Double", "String":"String", "ByteArray":"Blob" }/> <#assign primitiveTypes = ["boolean", "byte", "int", "long", "float", "double", "short"]/> <#macro multiIndexes> { <#list entity.multiIndexes as index> @Index(value = "${index.orderSpec}"<#if index.nonDefaultName>, name = "${index.name}"<#if index.unique>, unique = true)<#sep>, } package ${entity.javaPackage}; import org.greenrobot.greendao.annotation.*; <#if entity.toManyRelations?has_content> import java.util.List; <#if entity.active> import ${schema.defaultJavaPackageDao}.${schema.prefix}DaoSession; import org.greenrobot.greendao.DaoException; <#if entity.additionalImportsEntity?has_content> <#list entity.additionalImportsEntity as additionalImport> import ${additionalImport}; <#if entity.hasKeepSections> // THIS CODE IS GENERATED BY greenDAO, EDIT ONLY INSIDE THE "KEEP"-SECTIONS // KEEP INCLUDES - put your custom includes here <#if keepIncludes?has_content>${keepIncludes!}// KEEP INCLUDES END <#else> // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit. <#if entity.javaDoc ??> ${entity.javaDoc} <#else> /** * Entity mapped to table "${entity.dbName}". */ <#if entity.codeBeforeClass ??> ${entity.codeBeforeClass} <#assign entityAttrs = []> <#if schema.name != "default"><#assign entityAttrs = entityAttrs + ["schema = \"${schema.name}\""]> <#if entity.active><#assign entityAttrs = entityAttrs + ["active = true"]> <#if entity.nonDefaultDbName><#assign entityAttrs = entityAttrs + ["nameInDb = \"${entity.dbName}\""]> <#if (entity.multiIndexes?size > 0)> <#assign idxAttr>indexes = <@multiIndexes/> <#assign entityAttrs = entityAttrs + [idxAttr]> <#if entity.skipCreationInDb><#assign entityAttrs = entityAttrs + ["createInDb = false"]> @Entity<#if (entityAttrs?size > 0)>(${entityAttrs?join(", ")}) public class ${entity.className}<#if entity.superclass?has_content> extends ${entity.superclass} <#if entity.interfacesToImplement?has_content> implements <#list entity.interfacesToImplement as ifc>${ifc}<#if ifc_has_next>, { <#list entity.properties as property> <#assign notNull = property.notNull && !primitiveTypes?seq_contains(property.javaTypeInEntity)> <#if property.primaryKey||notNull||property.unique||property.index??||property.nonDefaultDbName||property.converter??> <#if property.javaDocField ??> ${property.javaDocField} <#if property.codeBeforeField ??> ${property.codeBeforeField} <#if property.primaryKey> @Id<#if property.autoincrement>(autoincrement = true) <#if property.nonDefaultDbName> @Property(nameInDb = "${property.dbName}") <#if property.converter??> @Convert(converter = ${property.converter}.class, columnType = ${property.javaType}.class) <#if notNull> @NotNull <#if property.unique> @Unique <#if ((property.index.nonDefaultName)!false) && (property.index.unique)!false> @Index(name = "${property.index.name}", unique = true) <#elseif (property.index.nonDefaultName)!false> @Index(name = "${property.index.name}") <#elseif (property.index.unique)!false> @Index(unique = true) <#elseif property.index??> @Index private ${property.javaTypeInEntity} ${property.propertyName}; <#if entity.active> /** Used to resolve relations */ @Generated private transient ${schema.prefix}DaoSession daoSession; /** Used for active entity operations. */ @Generated private transient ${entity.classNameDao} myDao; <#list entity.toOneRelations as toOne> <#if toOne.useFkProperty> @ToOne(joinProperty = "${toOne.fkProperties[0].propertyName}") private ${toOne.targetEntity.className} ${toOne.name}; @Generated private transient ${toOne.resolvedKeyJavaType[0]} ${toOne.name}__resolvedKey; <#else> @ToOne <#if toOne.fkProperties[0].nonDefaultDbName> @Property(nameInDb = "${toOne.fkProperties[0].dbName}") <#if toOne.fkProperties[0].unique> @Unique <#if toOne.fkProperties[0].notNull> @NotNull private ${toOne.targetEntity.className} ${toOne.name}; @Generated private transient boolean ${toOne.name}__refreshed; <#list entity.toManyRelations as toMany> <#if toMany.sourceProperties??> @ToMany(joinProperties = { <#list toMany.sourceProperties as sourceProperty> @JoinProperty(name = "${sourceProperty.propertyName}", referencedName = "${toMany.targetProperties[sourceProperty_index].propertyName}")<#sep>, }) <#elseif toMany.targetProperties??> @ToMany(mappedBy = "${toMany.targetProperties[0]}") <#else> @ToMany @JoinEntity(entity = ${toMany.joinEntity.className}.class, sourceProperty = "${toMany.sourceProperty.propertyName}", targetProperty = "${toMany.targetProperty.propertyName}") <#assign orderSpec = (toMany.orderSpec)!"0"> <#if orderSpec != "0"> @OrderBy("${orderSpec}") private List<${toMany.targetEntity.className}> ${toMany.name}; <#if entity.hasKeepSections> // KEEP FIELDS - put your custom fields here ${keepFields!} // KEEP FIELDS END <#if entity.constructors> @Generated public ${entity.className}() { } <#if entity.propertiesPk?has_content && entity.propertiesPk?size != entity.properties?size> public ${entity.className}(<#list entity.propertiesPk as property>${property.javaType} ${property.propertyName}<#if property_has_next>, ) { <#list entity.propertiesPk as property> this.${property.propertyName} = ${property.propertyName}; } @Generated public ${entity.className}(<#list entity.properties as property>${property.javaTypeInEntity} ${property.propertyName}<#if property_has_next>, ) { <#list entity.properties as property> this.${property.propertyName} = ${property.propertyName}; } <#if entity.active> /** called by internal mechanisms, do not call yourself. */ @Generated public void __setDaoSession(${schema.prefix}DaoSession daoSession) { this.daoSession = daoSession; myDao = daoSession != null ? daoSession.get${entity.classNameDao?cap_first}() : null; } <#list entity.properties as property> <#if property.javaDocGetter ??> ${property.javaDocGetter} <#if property.codeBeforeGetter ??> ${property.codeBeforeGetter} <#if property.notNull && !primitiveTypes?seq_contains(property.javaTypeInEntity)> @NotNull public ${property.javaTypeInEntity} get${property.propertyName?cap_first}() { return ${property.propertyName}; } <#if property.notNull && !primitiveTypes?seq_contains(property.javaTypeInEntity)> /** Not-null value; ensure this value is available before it is saved to the database. */ <#if property.javaDocSetter ??> ${property.javaDocSetter} <#if property.codeBeforeSetter ??> ${property.codeBeforeSetter} public void set${property.propertyName?cap_first}(<#if property.notNull && !primitiveTypes?seq_contains(property.javaTypeInEntity)>@NotNull ${property.javaTypeInEntity} ${property.propertyName}) { this.${property.propertyName} = ${property.propertyName}; } <#-- ########################################## ########## To-One Relations ############## ########################################## --> <#list entity.toOneRelations as toOne> /** To-one relationship, resolved on first access. */ @Generated public ${toOne.targetEntity.className} get${toOne.name?cap_first}() { <#if toOne.useFkProperty> ${toOne.fkProperties[0].javaType} __key = this.${toOne.fkProperties[0].propertyName}; if (${toOne.name}__resolvedKey == null || <#-- --><#if toOne.resolvedKeyUseEquals[0]>!${toOne.name}__resolvedKey.equals(__key)<#-- --><#else>${toOne.name}__resolvedKey != __key) { __throwIfDetached(); ${toOne.targetEntity.classNameDao} targetDao = daoSession.get${toOne.targetEntity.classNameDao?cap_first}(); ${toOne.targetEntity.className} ${toOne.name}New = targetDao.load(__key); synchronized (this) { ${toOne.name} = ${toOne.name}New; ${toOne.name}__resolvedKey = __key; } } <#else> if (${toOne.name} != null || !${toOne.name}__refreshed) { __throwIfDetached(); ${toOne.targetEntity.classNameDao} targetDao = daoSession.get${toOne.targetEntity.classNameDao?cap_first}(); targetDao.refresh(${toOne.name}); ${toOne.name}__refreshed = true; } return ${toOne.name}; } <#if !toOne.useFkProperty> /** To-one relationship, returned entity is not refreshed and may carry only the PK property. */ @Generated public ${toOne.targetEntity.className} peak${toOne.name?cap_first}() { return ${toOne.name}; } @Generated public void set${toOne.name?cap_first}(<#if toOne.fkProperties[0].notNull && !primitiveTypes?seq_contains(toOne.fkProperties[0].javaTypeInEntity)>@NotNull ${toOne.targetEntity.className} ${toOne.name}) { <#if toOne.fkProperties[0].notNull> if (${toOne.name} == null) { throw new DaoException("To-one property '${toOne.fkProperties[0].propertyName}' has not-null constraint; cannot set to-one to null"); } synchronized (this) { this.${toOne.name} = ${toOne.name}; <#if toOne.useFkProperty> ${toOne.fkProperties[0].propertyName} = <#if !toOne.fkProperties[0].notNull>${toOne.name} == null ? null : ${toOne.name}.get${toOne.targetEntity.pkProperty.propertyName?cap_first}(); ${toOne.name}__resolvedKey = ${toOne.fkProperties[0].propertyName}; <#else> ${toOne.name}__refreshed = true; } } <#-- ########################################## ########## To-Many Relations ############# ########################################## --> <#list entity.toManyRelations as toMany> /** To-many relationship, resolved on first access (and after reset). Changes to to-many relations are not persisted, make changes to the target entity. */ @Generated public List<${toMany.targetEntity.className}> get${toMany.name?cap_first}() { if (${toMany.name} == null) { __throwIfDetached(); ${toMany.targetEntity.classNameDao} targetDao = daoSession.get${toMany.targetEntity.classNameDao?cap_first}(); List<${toMany.targetEntity.className}> ${toMany.name}New = targetDao._query${toMany.sourceEntity.className?cap_first}_${toMany.name?cap_first}(<#-- --><#if toMany.sourceProperties??><#list toMany.sourceProperties as property>${property.propertyName}<#if property_has_next>, <#else><#-- -->${entity.pkProperty.propertyName}); synchronized (this) {<#-- Check if another thread was faster, we cannot lock while doing the query to prevent deadlocks --> if(${toMany.name} == null) { ${toMany.name} = ${toMany.name}New; } } } return ${toMany.name}; } /** Resets a to-many relationship, making the next get call to query for a fresh result. */ @Generated public synchronized void reset${toMany.name?cap_first}() { ${toMany.name} = null; } <#-- ########################################## ########## Active entity operations ###### ########################################## --> <#if entity.active> /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. * Entity must attached to an entity context. */ @Generated public void delete() { __throwIfDetached(); myDao.delete(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. * Entity must attached to an entity context. */ @Generated public void update() { __throwIfDetached(); myDao.update(this); } /** * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. * Entity must attached to an entity context. */ @Generated public void refresh() { __throwIfDetached(); myDao.refresh(this); } @Generated private void __throwIfDetached() { if (myDao == null) { throw new DaoException("Entity is detached from DAO context"); } } <#if entity.hasKeepSections> // KEEP METHODS - put your custom methods here ${keepMethods!} // KEEP METHODS END } ================================================ FILE: DaoGenerator/src-test/org/greenrobot/greendao/generator/SimpleDaoGeneratorTest.java ================================================ /* * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * This file is part of greenDAO Generator. * * greenDAO Generator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * greenDAO Generator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with greenDAO Generator. If not, see . */ package org.greenrobot.greendao.generator; import org.junit.Test; import java.io.File; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class SimpleDaoGeneratorTest { @Test public void testMinimalSchema() throws Exception { Schema schema = new Schema(1, "org.greenrobot.testdao"); Entity addressEntity = schema.addEntity("Addresse"); Property idProperty = addressEntity.addIdProperty().getProperty(); addressEntity.addIntProperty("count").index(); addressEntity.addIntProperty("dummy").notNull(); assertEquals(1, schema.getEntities().size()); assertEquals(3, addressEntity.getProperties().size()); File outputDir = new File("build/test-out"); outputDir.mkdirs(); File daoFile = new File(outputDir, "org/greenrobot/testdao/" + addressEntity.getClassName() + "Dao.java"); daoFile.delete(); assertFalse(daoFile.exists()); new DaoGenerator().generateAll(schema, outputDir.getPath()); assertEquals("PRIMARY KEY", idProperty.getConstraints()); assertTrue(daoFile.toString(), daoFile.exists()); } @Test public void testDbName() { assertEquals("NORMAL", DaoUtil.dbName("normal")); assertEquals("NORMAL", DaoUtil.dbName("Normal")); assertEquals("CAMEL_CASE", DaoUtil.dbName("CamelCase")); assertEquals("CAMEL_CASE_THREE", DaoUtil.dbName("CamelCaseThree")); assertEquals("CAMEL_CASE_XXXX", DaoUtil.dbName("CamelCaseXXXX")); } @Test(expected = RuntimeException.class) public void testInterfacesError() throws Exception { Schema schema = new Schema(1, "org.greenrobot.testdao"); Entity addressTable = schema.addEntity("Addresse"); addressTable.implementsInterface("Dummy"); addressTable.implementsInterface("Dummy"); } } ================================================ FILE: README.md ================================================ ⚠️ **This project is not longer actively maintained.** If you are looking for an easy to use and efficient database solution, please: Check out ObjectBox =================== **Check out our new mobile database [ObjectBox](https://objectbox.io/) ([GitHub repo](https://github.com/objectbox/objectbox-java)).** ObjectBox is a superfast object-oriented database with strong relation support. ObjectBox is embedded into your Android, Linux, macOS, or Windows app. greenDAO ======== greenDAO is a light & fast ORM for Android that maps objects to SQLite databases. Being highly optimized for Android, greenDAO offers great performance and consumes minimal memory. **Home page, documentation, and support links: https://greenrobot.org/greendao/** [![Build Status](https://travis-ci.org/greenrobot/greenDAO.svg?branch=master)](https://travis-ci.org/greenrobot/greenDAO) [![Follow greenrobot on Twitter](https://img.shields.io/twitter/follow/greenrobot_de.svg?style=flat-square&logo=twitter)](https://twitter.com/greenrobot_de) Features -------- greenDAO's unique set of features: * Rock solid: greenDAO has been around since 2011 and is used by countless famous apps * Super simple: concise and straight-forward API, in V3 with annotations * Small: The library is <150K and it's just plain Java jar (no CPU dependent native parts) * Fast: Probably the fastest ORM for Android, driven by intelligent code generation * Safe and expressive query API: QueryBuilder uses property constants to avoid typos * Powerful joins: query across entities and even chain joins for complex relations * Flexible property types: use custom classes or enums to represent data in your entity * Encryption: supports SQLCipher encrypted databases Add greenDAO to your project ---------------------------- greenDAO is available on Maven Central. Please ensure that you are using the latest versions of the [greendao](https://search.maven.org/search?q=g:org.greenrobot%20AND%20a:greendao) and [greendao-gradle-plugin](https://search.maven.org/search?q=g:org.greenrobot%20AND%20a:greendao-gradle-plugin) artifact. Add the following Gradle configuration to your Android project. In your root `build.gradle` file: ```groovy buildscript { repositories { jcenter() mavenCentral() // add repository } dependencies { classpath 'com.android.tools.build:gradle:' classpath 'org.greenrobot:greendao-gradle-plugin:3.3.1' // add plugin } } ``` In your app modules `app/build.gradle` file: ```groovy apply plugin: 'com.android.application' apply plugin: 'org.greenrobot.greendao' // apply plugin dependencies { implementation 'org.greenrobot:greendao:3.3.0' // add library } ``` Note that this hooks up the greenDAO Gradle plugin to your build process. When you build your project, it generates classes like DaoMaster, DaoSession and DAOs. Continue at the [Getting Started](https://greenrobot.org/greendao/documentation/how-to-get-started/) page. R8, ProGuard ------------ If your project uses R8 or ProGuard add the following rules: ```bash -keepclassmembers class * extends org.greenrobot.greendao.AbstractDao { public static java.lang.String TABLENAME; } -keep class **$Properties { *; } # If you DO use SQLCipher: -keep class org.greenrobot.greendao.database.SqlCipherEncryptedHelper { *; } # If you do NOT use SQLCipher: -dontwarn net.sqlcipher.database.** # If you do NOT use RxJava: -dontwarn rx.** ``` Homepage, Documentation, Links ------------------------------ For more details on greenDAO please check [greenDAO's website](https://greenrobot.org/greendao). Here are some direct links you may find useful: [Features](https://greenrobot.org/greendao/features/) [Getting Started](https://greenrobot.org/greendao/documentation/how-to-get-started/) [Documentation](https://greenrobot.org/greendao/documentation/) [Changelog](https://greenrobot.org/greendao/changelog/) [Technical FAQ](https://greenrobot.org/greendao/documentation/technical-faq/) [Non-Technical FAQ](https://greenrobot.org/greendao/documentation/faq/) [Migrating to greenDAO 3](https://greenrobot.org/greendao/documentation/updating-to-greendao-3-and-annotations/) More Open Source by greenrobot ============================== [__ObjectBox__](https://github.com/objectbox/objectbox-java) is a new superfast object-oriented database for mobile. [__EventBus__](https://github.com/greenrobot/EventBus) is a central publish/subscribe bus for Android with optional delivery threads, priorities, and sticky events. A great tool to decouple components (e.g. Activities, Fragments, logic components) from each other. [__Essentials__](https://github.com/greenrobot/essentials) is a set of utility classes and hash functions for Android & Java projects. ================================================ FILE: build.gradle ================================================ // Sub projects may reference rootProject.version version = '3.3.0' wrapper { distributionType = Wrapper.DistributionType.ALL } ext { compileSdkVersion = 29 minSdkVersion = 7 targetSdkVersion = 25 // common dependencies for Android projects (not to be used in example projects for better copy and paste) dep = [ androidPlugin: 'com.android.tools.build:gradle:3.5.3', // Used only in tests, should point to the latest *published* version (not relevant to publishing) greendaoPlugin: 'org.greenrobot:greendao-gradle-plugin:3.3.1' ] } if (JavaVersion.current().isJava8Compatible()) { allprojects { tasks.withType(Javadoc) { options.addStringOption('Xdoclint:none', '-quiet') } } } subprojects { repositories { mavenCentral() jcenter() google() } } ================================================ FILE: examples/DaoExample/build.gradle ================================================ buildscript { repositories { jcenter() google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:3.5.3' classpath("org.greenrobot:greendao-gradle-plugin:3.3.1") } } apply plugin: 'com.android.application' apply plugin: 'org.greenrobot.greendao' android { compileSdkVersion 29 // To use deprecated test classes on SDK 28+. https://developer.android.com/training/testing/set-up-project useLibrary 'android.test.runner' useLibrary 'android.test.base' compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } defaultConfig { applicationId "org.greenrobot.greendao.example" minSdkVersion 15 targetSdkVersion 29 versionCode 1 versionName "3" testInstrumentationRunner "android.test.InstrumentationTestRunner" } } greendao { schemaVersion 1000 } dependencies { implementation 'org.greenrobot:greendao:3.3.0' // optional: add if you want to use encrypted databases, see the App class for details // implementation 'net.zetetic:android-database-sqlcipher:3.5.6' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' } uploadArchives.enabled = false ================================================ FILE: examples/DaoExample/src/androidTest/java/org/greenrobot/greendao/example/NoteTest.java ================================================ /* * Copyright (C) 2011 Markus Junginger, greenrobot (http://greenrobot.de) * * 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. */ package org.greenrobot.greendao.example; import org.greenrobot.greendao.test.AbstractDaoTestLongPk; public class NoteTest extends AbstractDaoTestLongPk { public NoteTest() { super(NoteDao.class); } @Override protected Note createEntity(Long key) { Note entity = new Note(); entity.setId(key); entity.setText("green note"); // Has to be set as it is "not null" return entity; } } ================================================ FILE: examples/DaoExample/src/main/AndroidManifest.xml ================================================ ================================================ FILE: examples/DaoExample/src/main/java/org/greenrobot/greendao/example/App.java ================================================ package org.greenrobot.greendao.example; import android.app.Application; import android.content.Context; import org.greenrobot.greendao.database.Database; public class App extends Application { private DaoSession daoSession; @Override public void onCreate() { super.onCreate(); // regular SQLite database ExampleOpenHelper helper = new ExampleOpenHelper(this, "notes-db"); Database db = helper.getWritableDb(); // encrypted SQLCipher database // note: you need to add SQLCipher to your dependencies, check the build.gradle file // ExampleOpenHelper helper = new ExampleOpenHelper(this, "notes-db-encrypted"); // Database db = helper.getEncryptedWritableDb("encryption-key"); daoSession = new DaoMaster(db).newSession(); } public DaoSession getDaoSession() { return daoSession; } public static class ExampleOpenHelper extends DaoMaster.OpenHelper { public ExampleOpenHelper(Context context, String name) { super(context, name); } @Override public void onCreate(Database db) { super.onCreate(db); // Insert some example data. // INSERT INTO NOTE (_id, DATE, TEXT) VALUES(1, 0, 'Example Note') db.execSQL("INSERT INTO " + NoteDao.TABLENAME + " (" + NoteDao.Properties.Id.columnName + ", " + NoteDao.Properties.Date.columnName + ", " + NoteDao.Properties.Text.columnName + ") VALUES(1, 0, 'Example Note')"); } } } ================================================ FILE: examples/DaoExample/src/main/java/org/greenrobot/greendao/example/Note.java ================================================ package org.greenrobot.greendao.example; import org.greenrobot.greendao.annotation.Convert; import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.annotation.Id; import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.NotNull; /** * Entity mapped to table "NOTE". */ @Entity(indexes = { @Index(value = "text, date DESC", unique = true) }) public class Note { @Id private Long id; @NotNull private String text; private String comment; private java.util.Date date; @Convert(converter = NoteTypeConverter.class, columnType = String.class) private NoteType type; @Generated(hash = 1272611929) public Note() { } public Note(Long id) { this.id = id; } @Generated(hash = 1686394253) public Note(Long id, @NotNull String text, String comment, java.util.Date date, NoteType type) { this.id = id; this.text = text; this.comment = comment; this.date = date; this.type = type; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } @NotNull public String getText() { return text; } /** Not-null value; ensure this value is available before it is saved to the database. */ public void setText(@NotNull String text) { this.text = text; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public java.util.Date getDate() { return date; } public void setDate(java.util.Date date) { this.date = date; } public NoteType getType() { return type; } public void setType(NoteType type) { this.type = type; } } ================================================ FILE: examples/DaoExample/src/main/java/org/greenrobot/greendao/example/NoteActivity.java ================================================ /* * Copyright (C) 2011 Markus Junginger, greenrobot (http://greenrobot.de) * * 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. */ package org.greenrobot.greendao.example; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.View; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import org.greenrobot.greendao.query.Query; import java.text.DateFormat; import java.util.Date; import java.util.List; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; public class NoteActivity extends AppCompatActivity { private EditText editText; private View addNoteButton; private NoteDao noteDao; private Query notesQuery; private NotesAdapter notesAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); setUpViews(); // get the note DAO DaoSession daoSession = ((App) getApplication()).getDaoSession(); noteDao = daoSession.getNoteDao(); // query all notes, sorted a-z by their text notesQuery = noteDao.queryBuilder().orderAsc(NoteDao.Properties.Text).build(); updateNotes(); } private void updateNotes() { List notes = notesQuery.list(); notesAdapter.setNotes(notes); } protected void setUpViews() { RecyclerView recyclerView = findViewById(R.id.recyclerViewNotes); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(this)); notesAdapter = new NotesAdapter(noteClickListener); recyclerView.setAdapter(notesAdapter); addNoteButton = findViewById(R.id.buttonAdd); addNoteButton.setEnabled(false); editText = findViewById(R.id.editTextNote); editText.setOnEditorActionListener((v, actionId, event) -> { if (actionId == EditorInfo.IME_ACTION_DONE) { addNote(); return true; } return false; }); editText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { boolean enable = s.length() != 0; addNoteButton.setEnabled(enable); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); } public void onAddButtonClick(View view) { addNote(); } private void addNote() { String noteText = editText.getText().toString(); editText.setText(""); final DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); String comment = "Added on " + df.format(new Date()); Note note = new Note(); note.setText(noteText); note.setComment(comment); note.setDate(new Date()); note.setType(NoteType.TEXT); noteDao.insert(note); Log.d("DaoExample", "Inserted new note, ID: " + note.getId()); updateNotes(); } NotesAdapter.NoteClickListener noteClickListener = new NotesAdapter.NoteClickListener() { @Override public void onNoteClick(int position) { Note note = notesAdapter.getNote(position); Long noteId = note.getId(); noteDao.deleteByKey(noteId); Log.d("DaoExample", "Deleted note, ID: " + noteId); updateNotes(); } }; } ================================================ FILE: examples/DaoExample/src/main/java/org/greenrobot/greendao/example/NoteType.java ================================================ package org.greenrobot.greendao.example; public enum NoteType { TEXT, LIST, PICTURE } ================================================ FILE: examples/DaoExample/src/main/java/org/greenrobot/greendao/example/NoteTypeConverter.java ================================================ package org.greenrobot.greendao.example; import org.greenrobot.greendao.converter.PropertyConverter; public class NoteTypeConverter implements PropertyConverter { @Override public NoteType convertToEntityProperty(String databaseValue) { return NoteType.valueOf(databaseValue); } @Override public String convertToDatabaseValue(NoteType entityProperty) { return entityProperty.name(); } } ================================================ FILE: examples/DaoExample/src/main/java/org/greenrobot/greendao/example/NotesAdapter.java ================================================ package org.greenrobot.greendao.example; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.ArrayList; import java.util.List; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; public class NotesAdapter extends RecyclerView.Adapter { private NoteClickListener clickListener; private List dataset; public interface NoteClickListener { void onNoteClick(int position); } static class NoteViewHolder extends RecyclerView.ViewHolder { public TextView text; public TextView comment; public NoteViewHolder(View itemView, final NoteClickListener clickListener) { super(itemView); text = itemView.findViewById(R.id.textViewNoteText); comment = itemView.findViewById(R.id.textViewNoteComment); itemView.setOnClickListener(view -> { if (clickListener != null) { clickListener.onNoteClick(getAdapterPosition()); } }); } } public NotesAdapter(NoteClickListener clickListener) { this.clickListener = clickListener; this.dataset = new ArrayList<>(); } public void setNotes(@NonNull List notes) { dataset = notes; notifyDataSetChanged(); } public Note getNote(int position) { return dataset.get(position); } @NonNull @Override public NotesAdapter.NoteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_note, parent, false); return new NoteViewHolder(view, clickListener); } @Override public void onBindViewHolder(NotesAdapter.NoteViewHolder holder, int position) { Note note = dataset.get(position); holder.text.setText(note.getText()); holder.comment.setText(note.getComment()); } @Override public int getItemCount() { return dataset.size(); } } ================================================ FILE: examples/DaoExample/src/main/res/layout/item_note.xml ================================================ ================================================ FILE: examples/DaoExample/src/main/res/layout/main.xml ================================================