119b41105359a52aeb80070dec40247241231f05dYigit Boyar/*
219b41105359a52aeb80070dec40247241231f05dYigit Boyar * Copyright (C) 2016 The Android Open Source Project
319b41105359a52aeb80070dec40247241231f05dYigit Boyar *
419b41105359a52aeb80070dec40247241231f05dYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
519b41105359a52aeb80070dec40247241231f05dYigit Boyar * you may not use this file except in compliance with the License.
619b41105359a52aeb80070dec40247241231f05dYigit Boyar * You may obtain a copy of the License at
719b41105359a52aeb80070dec40247241231f05dYigit Boyar *
819b41105359a52aeb80070dec40247241231f05dYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
919b41105359a52aeb80070dec40247241231f05dYigit Boyar *
1019b41105359a52aeb80070dec40247241231f05dYigit Boyar * Unless required by applicable law or agreed to in writing, software
1119b41105359a52aeb80070dec40247241231f05dYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
1219b41105359a52aeb80070dec40247241231f05dYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1319b41105359a52aeb80070dec40247241231f05dYigit Boyar * See the License for the specific language governing permissions and
1419b41105359a52aeb80070dec40247241231f05dYigit Boyar * limitations under the License.
1519b41105359a52aeb80070dec40247241231f05dYigit Boyar */
1619b41105359a52aeb80070dec40247241231f05dYigit Boyar
17bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverettepackage androidx.room.processor
18bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverette
19bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Delete
20bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Insert
21bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Query
22bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.RawQuery
23bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Update
24bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.ext.RoomTypeNames
25bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.ext.SupportDbTypeNames
26bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.parser.SQLTypeAffinity
27bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.vo.CustomTypeConverter
28bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.vo.Field
2988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarimport com.squareup.javapoet.TypeName
3019b41105359a52aeb80070dec40247241231f05dYigit Boyar
3119b41105359a52aeb80070dec40247241231f05dYigit Boyarobject ProcessorErrors {
322c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    private fun String.trim(): String {
33b78956d6a56230a8c2f86b6e749d35e880e4d6efshepshapard        return this.trimIndent().replace("\n", " ")
342c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    }
3519b41105359a52aeb80070dec40247241231f05dYigit Boyar    val MISSING_QUERY_ANNOTATION = "Query methods must be annotated with ${Query::class.java}"
364f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    val MISSING_INSERT_ANNOTATION = "Insertion methods must be annotated with ${Insert::class.java}"
37958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar    val MISSING_DELETE_ANNOTATION = "Deletion methods must be annotated with ${Delete::class.java}"
3874b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    val MISSING_UPDATE_ANNOTATION = "Update methods must be annotated with ${Update::class.java}"
39de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    val MISSING_RAWQUERY_ANNOTATION = "RawQuery methods must be annotated with" +
40de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            " ${RawQuery::class.java}"
41333b4b5e49c48adf7fb928d445b6f7f276b54a02Yigit Boyar    val INVALID_ON_CONFLICT_VALUE = "On conflict value must be one of @OnConflictStrategy values."
424f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    val INVALID_INSERTION_METHOD_RETURN_TYPE = "Methods annotated with @Insert can return either" +
43dd6aaec8254abfee2dd82a5f2c59282f8eeee9eeYuichi Araki            " void, long, Long, long[], Long[] or List<Long>."
4425b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar    val TRANSACTION_REFERENCE_DOCS = "https://developer.android.com/reference/android/arch/" +
4525b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar            "persistence/room/Transaction.html"
46f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar
476f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun insertionMethodReturnTypeMismatch(definedReturn: TypeName,
486f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                                          expectedReturnTypes: List<TypeName>): String {
49f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar        return "Method returns $definedReturn but it should return one of the following: `" +
50f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                expectedReturnTypes.joinToString(", ") + "`. If you want to return the list of" +
51f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar                " row ids from the query, your insertion method can receive only 1 parameter."
52f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar    }
53f5f2cf6b9ed63915448e81551e4b7bb72a26030eYigit Boyar
544f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    val ABSTRACT_METHOD_IN_DAO_MISSING_ANY_ANNOTATION = "Abstract method in DAO must be annotated" +
554f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            " with ${Query::class.java} AND ${Insert::class.java}"
56c24f4573b0732b6cc0569fc3bbe3867be6f4880cYigit Boyar    val INVALID_ANNOTATION_COUNT_IN_DAO_METHOD = "An abstract DAO method must be" +
57c24f4573b0732b6cc0569fc3bbe3867be6f4880cYigit Boyar            " annotated with one and only one of the following annotations: " +
58c24f4573b0732b6cc0569fc3bbe3867be6f4880cYigit Boyar            DaoProcessor.PROCESSED_ANNOTATIONS.joinToString(",") {
59958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar        it.java.simpleName
60958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar    }
6119b41105359a52aeb80070dec40247241231f05dYigit Boyar    val CANNOT_RESOLVE_RETURN_TYPE = "Cannot resolve return type for %s"
624f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    val CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_METHODS = "Cannot use unbound generics in query" +
634f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            " methods. It must be bound to a type through base Dao class."
644f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    val CANNOT_USE_UNBOUND_GENERICS_IN_INSERTION_METHODS = "Cannot use unbound generics in" +
654f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            " insertion methods. It must be bound to a type through base Dao class."
66de33ce4068e2678c03fa6fd62f4770be89f79adcYigit Boyar    val CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS = "Cannot use unbound fields in entities."
670f77cff2005ccc6263b9902b3ea56fe01161ba51Yigit Boyar    val CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES = "Cannot use unbound generics in Dao classes." +
680f77cff2005ccc6263b9902b3ea56fe01161ba51Yigit Boyar            " If you are trying to create a base DAO, create a normal class, extend it with type" +
690f77cff2005ccc6263b9902b3ea56fe01161ba51Yigit Boyar            " params then mark the subclass with @Dao."
701600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar    val CANNOT_FIND_GETTER_FOR_FIELD = "Cannot find getter for field."
711600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar    val CANNOT_FIND_SETTER_FOR_FIELD = "Cannot find setter for field."
72e6325fbeaa2e6759496ea2ca9a4d3d958df690d7Yigit Boyar    val MISSING_PRIMARY_KEY = "An entity must have at least 1 field annotated with @PrimaryKey"
732c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    val AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT = "If a primary key is annotated with" +
742c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar            " autoGenerate, its type must be int, Integer, long or Long."
754d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar    val AUTO_INCREMENT_EMBEDDED_HAS_MULTIPLE_FIELDS = "When @PrimaryKey annotation is used on a" +
764d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar            " field annotated with @Embedded, the embedded class should have only 1 field."
772c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar
782c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun multiplePrimaryKeyAnnotations(primaryKeys: List<String>): String {
792c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar        return """
802c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar                You cannot have multiple primary keys defined in an Entity. If you
812c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar                want to declare a composite primary key, you should use @Entity#primaryKeys and
822c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar                not use @PrimaryKey. Defined Primary Keys:
832c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar                ${primaryKeys.joinToString(", ")}""".trim()
842c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    }
852c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar
862c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun primaryKeyColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
872c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar        return "$columnName referenced in the primary key does not exists in the Entity." +
882c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar                " Available column names:${allColumns.joinToString(", ")}"
892c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    }
902c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar
910f77cff2005ccc6263b9902b3ea56fe01161ba51Yigit Boyar    val DAO_MUST_BE_AN_ABSTRACT_CLASS_OR_AN_INTERFACE = "Dao class must be an abstract class or" +
920f77cff2005ccc6263b9902b3ea56fe01161ba51Yigit Boyar            " an interface"
938bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar    val DATABASE_MUST_BE_ANNOTATED_WITH_DATABASE = "Database must be annotated with @Database"
948bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar    val DAO_MUST_BE_ANNOTATED_WITH_DAO = "Dao class must be annotated with @Dao"
9517caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki
9617caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki    fun daoMustHaveMatchingConstructor(daoName: String, dbName: String): String {
9717caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki        return """
9817caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki                $daoName needs to have either an empty constructor or a constructor that takes
9917caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki                $dbName as its only parameter.
10017caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki                """.trim()
10117caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki    }
10217caba59e1fd850fe1381d7311d23afc4e07cdfbYuichi Araki
1038bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar    val ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY = "Entity class must be annotated with @Entity"
1048bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar    val DATABASE_ANNOTATION_MUST_HAVE_LIST_OF_ENTITIES = "@Database annotation must specify list" +
1058bad027c789d3fb3da8e68fa0154f2a24ccc2865Yigit Boyar            " of entities"
1060fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar    val COLUMN_NAME_CANNOT_BE_EMPTY = "Column name cannot be blank. If you don't want to set it" +
107275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            ", just remove the @ColumnInfo annotation or use @ColumnInfo.INHERIT_FIELD_NAME."
1080fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar
1090fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar    val ENTITY_TABLE_NAME_CANNOT_BE_EMPTY = "Entity table name cannot be blank. If you don't want" +
1100fc66ddc60bdc71d5466bb1db1a218e5a3d9c1fcYigit Boyar            " to set it, just remove the tableName property."
1110f77cff2005ccc6263b9902b3ea56fe01161ba51Yigit Boyar
112275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val CANNOT_BIND_QUERY_PARAMETER_INTO_STMT = "Query method parameters should either be a" +
113275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            " type that can be converted into a database column or a List / Array that contains" +
114275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            " such type. You can consider adding a Type Adapter for this."
115250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar
1164f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    val QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE = "Query/Insert method parameters cannot " +
1174f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            "start with underscore (_)."
1188e543c445cb5559e579f54c1ac00d0ca83ec3fbbYigit Boyar
119efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar    val CANNOT_FIND_QUERY_RESULT_ADAPTER = "Not sure how to convert a Cursor to this method's " +
120efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar            "return type"
121efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
1224f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar    val INSERTION_DOES_NOT_HAVE_ANY_PARAMETERS_TO_INSERT = "Method annotated with" +
1234f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar            " @Insert but does not have any parameters to insert."
1244f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
12574b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    val DELETION_MISSING_PARAMS = "Method annotated with" +
126958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            " @Delete but does not have any parameters to delete."
127958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar
12874b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    val UPDATE_MISSING_PARAMS = "Method annotated with" +
12974b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            " @Update but does not have any parameters to update."
13074b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar
131b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki    val TRANSACTION_METHOD_MODIFIERS = "Method annotated with @Transaction must not be " +
13225b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar            "private, final, or abstract. It can be abstract only if the method is also" +
13325b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar            " annotated with @Query."
13425b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar
13525b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar    val TRANSACTION_MISSING_ON_RELATION = "The return value includes a Pojo with a @Relation." +
13625b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar            " It is usually desired to annotate this method with @Transaction to avoid" +
13725b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar            " possibility of inconsistent results between the Pojo and its relations. See " +
13825b465c796ebee5bd7d304becbcf6a42fed53056Yigit Boyar            TRANSACTION_REFERENCE_DOCS + " for details."
139b3c4d9308e4fd66beca3a7824a5db749ce2aace1Yuichi Araki
140958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar    val CANNOT_FIND_ENTITY_FOR_SHORTCUT_QUERY_PARAMETER = "Type of the parameter must be a class " +
141958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            "annotated with @Entity or a collection/array of it."
1424f0db7db556b473393dfc31bba5ea67def574877Yigit Boyar
143291985054e698c918df1c735d1042b63b9e97219Yigit Boyar    val DB_MUST_EXTEND_ROOM_DB = "Classes annotated with @Database should extend " +
144291985054e698c918df1c735d1042b63b9e97219Yigit Boyar            RoomTypeNames.ROOM_DB
145291985054e698c918df1c735d1042b63b9e97219Yigit Boyar
146846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar    val LIVE_DATA_QUERY_WITHOUT_SELECT = "LiveData return type can only be used with SELECT" +
147846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar            " queries."
148846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar
1498df194b42f73f85f29760e8abe11d879ec49b286Yigit Boyar    val OBSERVABLE_QUERY_NOTHING_TO_OBSERVE = "Observable query return type (LiveData, Flowable" +
150cab865bed3b4e9df8b86b0b16c589c4e6dd0b71dYigit Boyar            ", DataSource, DataSourceFactory etc) can only be used with SELECT queries that" +
151cab865bed3b4e9df8b86b0b16c589c4e6dd0b71dYigit Boyar            " directly or indirectly (via @Relation, for example) access at least one table. For" +
152cab865bed3b4e9df8b86b0b16c589c4e6dd0b71dYigit Boyar            " @RawQuery, you should specify the list of tables to be observed via the" +
153cab865bed3b4e9df8b86b0b16c589c4e6dd0b71dYigit Boyar            " observedEntities field."
1542c56b466fc39da8bc5cb82dc494e534768e65eabshepshapard
1551a74519922de68e007027d56aae9370ee21f31f9shepshapard    val RECURSIVE_REFERENCE_DETECTED = "Recursive referencing through @Embedded and/or @Relation " +
1561a74519922de68e007027d56aae9370ee21f31f9shepshapard            "detected: %s"
1571a74519922de68e007027d56aae9370ee21f31f9shepshapard
158250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar    private val TOO_MANY_MATCHING_GETTERS = "Ambiguous getter for %s. All of the following " +
159250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar            "match: %s. You can @Ignore the ones that you don't want to match."
160efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
161efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar    fun tooManyMatchingGetters(field: Field, methodNames: List<String>): String {
1621600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar        return TOO_MANY_MATCHING_GETTERS.format(field, methodNames.joinToString(", "))
1631600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar    }
1641600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar
165250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar    private val TOO_MANY_MATCHING_SETTERS = "Ambiguous setter for %s. All of the following " +
166250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar            "match: %s. You can @Ignore the ones that you don't want to match."
167efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
168efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar    fun tooManyMatchingSetter(field: Field, methodNames: List<String>): String {
1691600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar        return TOO_MANY_MATCHING_SETTERS.format(field, methodNames.joinToString(", "))
1701600cc11df868b62b6ae3995d94a3ec0b86559adYigit Boyar    }
171250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar
172275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val CANNOT_FIND_COLUMN_TYPE_ADAPTER = "Cannot figure out how to save this field into" +
173275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            " database. You can consider adding a type converter for it."
174275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
175275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val CANNOT_FIND_STMT_BINDER = "Cannot figure out how to bind this field into a statement."
176275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
177275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val CANNOT_FIND_CURSOR_READER = "Cannot figure out how to read this field from a cursor."
178275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
179250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar    private val MISSING_PARAMETER_FOR_BIND = "Each bind variable in the query must have a" +
180250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar            " matching method parameter. Cannot find method parameters for %s."
181efaf86afac3163868eda7f91a1c04e3f6e6d7520Yigit Boyar
182250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar    fun missingParameterForBindVariable(bindVarName: List<String>): String {
183250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar        return MISSING_PARAMETER_FOR_BIND.format(bindVarName.joinToString(", "))
184250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar    }
185250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar
186250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar    private val UNUSED_QUERY_METHOD_PARAMETER = "Unused parameter%s: %s"
187250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar    fun unusedQueryMethodParameter(unusedParams: List<String>): String {
188250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar        return UNUSED_QUERY_METHOD_PARAMETER.format(
189250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar                if (unusedParams.size > 1) "s" else "",
190250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar                unusedParams.joinToString(","))
191250a3e6dc5d50c533575b7d276730b89eecc7c19Yigit Boyar    }
192af2292b2f53204a3004f53201f51908d70d8090eYigit Boyar
193af2292b2f53204a3004f53201f51908d70d8090eYigit Boyar    private val DUPLICATE_TABLES = "Table name \"%s\" is used by multiple entities: %s"
19413a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar    fun duplicateTableNames(tableName: String, entityNames: List<String>): String {
195af2292b2f53204a3004f53201f51908d70d8090eYigit Boyar        return DUPLICATE_TABLES.format(tableName, entityNames.joinToString(", "))
196af2292b2f53204a3004f53201f51908d70d8090eYigit Boyar    }
197958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar
198958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar    val DELETION_METHODS_MUST_RETURN_VOID_OR_INT = "Deletion methods must either return void or" +
199958df7dd95c2cecf93cacef6998a4d7e8d39b7efYigit Boyar            " return int (the number of deleted rows)."
20088865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar
20174b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar    val UPDATE_METHODS_MUST_RETURN_VOID_OR_INT = "Update methods must either return void or" +
20274b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar            " return int (the number of updated rows)."
20374b28faea4bcc4b7fab113a61a066d22dfae7258Yigit Boyar
20488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar    val DAO_METHOD_CONFLICTS_WITH_OTHERS = "Dao method has conflicts."
20588865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar
20613a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar    fun duplicateDao(dao: TypeName, methodNames: List<String>): String {
20788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        return """
208275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                All of these functions [${methodNames.joinToString(", ")}] return the same DAO
209275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                class [$dao].
21013a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                A database can use a DAO only once so you should remove ${methodNames.size - 1} of
21113a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                these conflicting DAO methods. If you are implementing any of these to fulfill an
21213a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                interface, don't make it abstract, instead, implement the code that calls the
21313a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                other one.
2142c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar                """.trim()
21513a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar    }
216afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar
217afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar    fun pojoMissingNonNull(pojoTypeName: TypeName, missingPojoFields: List<String>,
2186f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                           allQueryColumns: List<String>): String {
219afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar        return """
220afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar        The columns returned by the query does not have the fields
221afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar        [${missingPojoFields.joinToString(",")}] in $pojoTypeName even though they are
222afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar        annotated as non-null or primitive.
223afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar        Columns returned by the query: [${allQueryColumns.joinToString(",")}]
224afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar        """.trim()
225afbbe0af09599e93010b776bf91f54e82f23e7a3Yigit Boyar    }
22613a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar
22713a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar    fun cursorPojoMismatch(pojoTypeName: TypeName,
22813a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                           unusedColumns: List<String>, allColumns: List<String>,
22913a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                           unusedFields: List<Field>, allFields: List<Field>): String {
2302c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar        val unusedColumnsWarning = if (unusedColumns.isNotEmpty()) {
2312c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar            """
232275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                The query returns some columns [${unusedColumns.joinToString(", ")}] which are not
233275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                use by $pojoTypeName. You can use @ColumnInfo annotation on the fields to specify
23413a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                the mapping.
2352c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar            """.trim()
23613a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar        } else {
23713a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar            ""
23813a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar        }
2392c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar        val unusedFieldsWarning = if (unusedFields.isNotEmpty()) {
2402c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar            """
24113a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                $pojoTypeName has some fields
242275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                [${unusedFields.joinToString(", ") { it.columnName }}] which are not returned by the
24313a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                query. If they are not supposed to be read from the result, you can mark them with
24413a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar                @Ignore annotation.
2452c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar            """.trim()
24613a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar        } else {
24713a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar            ""
24813a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar        }
24913a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar        return """
25013a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar            $unusedColumnsWarning
25113a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar            $unusedFieldsWarning
25213a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar            You can suppress this warning by annotating the method with
25313a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar            @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH).
25413a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar            Columns returned by the query: ${allColumns.joinToString(", ")}.
25513a2048db98b1cc2dbd1692b73b794527975a446Yigit Boyar            Fields in $pojoTypeName: ${allFields.joinToString(", ") { it.columnName }}.
2562c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar            """.trim()
25788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar    }
25888865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar
259275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val TYPE_CONVERTER_UNBOUND_GENERIC = "Cannot use unbound generics in Type Converters."
260275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val TYPE_CONVERTER_BAD_RETURN_TYPE = "Invalid return type for a type converter."
261275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val TYPE_CONVERTER_MUST_RECEIVE_1_PARAM = "Type converters must receive 1 parameter."
262275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val TYPE_CONVERTER_EMPTY_CLASS = "Class is referenced as a converter but it does not have any" +
263275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            " converter methods."
264275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR = "Classes that are used as TypeConverters must" +
265275e7088223c097c1a2df718455bede42bc9efedYigit Boyar            " have no-argument public constructors."
266275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    val TYPE_CONVERTER_MUST_BE_PUBLIC = "Type converters must be public."
267275e7088223c097c1a2df718455bede42bc9efedYigit Boyar
2682c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun duplicateTypeConverters(converters: List<CustomTypeConverter>): String {
269275e7088223c097c1a2df718455bede42bc9efedYigit Boyar        return "Multiple methods define the same conversion. Conflicts with these:" +
270275e7088223c097c1a2df718455bede42bc9efedYigit Boyar                " ${converters.joinToString(", ") { it.toString() }}"
271275e7088223c097c1a2df718455bede42bc9efedYigit Boyar    }
27296cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar
27396cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar    // TODO must print field paths.
27496cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar    val POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME = "Field has non-unique column name."
27596cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar
2762c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun pojoDuplicateFieldNames(columnName: String, fieldPaths: List<String>): String {
27796cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar        return "Multiple fields have the same columnName: $columnName." +
27896cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar                " Field names: ${fieldPaths.joinToString(", ")}."
27996cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar    }
28096cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar
2814d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar    fun embeddedPrimaryKeyIsDropped(entityQName: String, fieldName: String): String {
28296cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar        return "Primary key constraint on $fieldName is ignored when being merged into " +
28396cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar                entityQName
28496cc740203eaa752fc85ca7ca722a8de550ae88cYigit Boyar    }
285dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar
286dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    val INDEX_COLUMNS_CANNOT_BE_EMPTY = "List of columns in an index cannot be empty"
287dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar
2882c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun indexColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
289dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar        return "$columnName referenced in the index does not exists in the Entity." +
290dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " Available column names:${allColumns.joinToString(", ")}"
291dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    }
292dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar
2932c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun duplicateIndexInEntity(indexName: String): String {
294dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar        return "There are multiple indices with name $indexName. This happen if you've declared" +
295dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " the same index multiple times or different indices have the same name. See" +
296dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " @Index documentation for details."
297dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    }
298dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar
2992c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun duplicateIndexInDatabase(indexName: String, indexPaths: List<String>): String {
300dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar        return "There are multiple indices with name $indexName. You should rename " +
301dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                "${indexPaths.size - 1} of these to avoid the conflict:" +
302dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                "${indexPaths.joinToString(", ")}."
303dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    }
304dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar
3054d4bae3f216e55f824d7d7fbfe2f8861906ee3e2Yigit Boyar    fun droppedEmbeddedFieldIndex(fieldPath: String, grandParent: String): String {
306dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar        return "The index will be dropped when being merged into $grandParent" +
307dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                "($fieldPath). You must re-declare it in $grandParent if you want to index this" +
308dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " field in $grandParent."
309dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    }
310dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar
3116f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun droppedEmbeddedIndex(entityName: String, fieldPath: String, grandParent: String): String {
312dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar        return "Indices defined in $entityName will be dropped when it is merged into" +
313dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " $grandParent ($fieldPath). You can re-declare them in $grandParent."
314dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    }
315dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar
3162c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun droppedSuperClassIndex(childEntity: String, superEntity: String): String {
317dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar        return "Indices defined in $superEntity will NOT be re-used in $childEntity. If you want" +
318dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " to inherit them, you must re-declare them in $childEntity." +
319dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " Alternatively, you can set inheritSuperIndices to true in the @Entity annotation."
320dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    }
321dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar
3222c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar    fun droppedSuperClassFieldIndex(fieldName: String, childEntity: String,
3232c6462f129bf43965ed8b054b026f6a28fe6fd8fYigit Boyar                                    superEntity: String): String {
324dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar        return "Index defined on field `$fieldName` in $superEntity will NOT be re-used in" +
325dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " $childEntity. " +
326dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                "If you want to inherit it, you must re-declare it in $childEntity." +
327dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar                " Alternatively, you can set inheritSuperIndices to true in the @Entity annotation."
328dc18ce63fe07921b1080e48d3e597e2b5240d17aYigit Boyar    }
329092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar
330092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    val RELATION_NOT_COLLECTION = "Fields annotated with @Relation must be a List or Set."
331092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar
3326f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun relationCannotFindEntityField(entityName: String, columnName: String,
3336f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                                      availableColumns: List<String>): String {
334f385ca501bbfd3ccaf6b412f8f09c64d9ee742f2Yigit Boyar        return "Cannot find the child entity column `$columnName` in $entityName." +
335f385ca501bbfd3ccaf6b412f8f09c64d9ee742f2Yigit Boyar                " Options: ${availableColumns.joinToString(", ")}"
336092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    }
337092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar
3386f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun relationCannotFindParentEntityField(entityName: String, columnName: String,
3396f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                                            availableColumns: List<String>): String {
340f385ca501bbfd3ccaf6b412f8f09c64d9ee742f2Yigit Boyar        return "Cannot find the parent entity column `$columnName` in $entityName." +
341f385ca501bbfd3ccaf6b412f8f09c64d9ee742f2Yigit Boyar                " Options: ${availableColumns.joinToString(", ")}"
342092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    }
343092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar
344092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    val RELATION_IN_ENTITY = "Entities cannot have relations."
345092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar
346092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    val CANNOT_FIND_TYPE = "Cannot find type."
347092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar
348f385ca501bbfd3ccaf6b412f8f09c64d9ee742f2Yigit Boyar    fun relationAffinityMismatch(parentColumn: String, childColumn: String,
3496f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                                 parentAffinity: SQLTypeAffinity?,
3506f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                                 childAffinity: SQLTypeAffinity?): String {
351092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar        return """
352f385ca501bbfd3ccaf6b412f8f09c64d9ee742f2Yigit Boyar        The affinity of parent column ($parentColumn : $parentAffinity) does not match the type
353f385ca501bbfd3ccaf6b412f8f09c64d9ee742f2Yigit Boyar        affinity of the child column ($childColumn : $childAffinity).
354092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar        """.trim()
355092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    }
356092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar
357092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    val CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION = "A field can be annotated with only" +
358092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar            " one of the following:" + PojoProcessor.PROCESSED_ANNOTATIONS.joinToString(",") {
359092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar        it.java.simpleName
360092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    }
361092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar
3626f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun relationBadProject(entityQName: String, missingColumnNames: List<String>,
3636f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                           availableColumnNames: List<String>): String {
364092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar        return """
365092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar        $entityQName does not have the following columns: ${missingColumnNames.joinToString(",")}.
366092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar        Available columns are: ${availableColumnNames.joinToString(",")}
367092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar        """.trim()
368092164e5501d0a254001225acd9dca42e5fa57e9Yigit Boyar    }
369a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar
370a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar    val MISSING_SCHEMA_EXPORT_DIRECTORY = "Schema export directory is not provided to the" +
371a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar            " annotation processor so we cannot export the schema. You can either provide" +
372a64756a18111a7312b3fa03b76d13381a8907176Yigit Boyar            " `room.schemaLocation` annotation processor argument OR set exportSchema to false."
373875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
374875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    val INVALID_FOREIGN_KEY_ACTION = "Invalid foreign key action. It must be one of the constants" +
375875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar            " defined in ForeignKey.Action"
376875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
3776f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun foreignKeyNotAnEntity(className: String): String {
378875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        return """
379875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        Classes referenced in Foreign Key annotations must be @Entity classes. $className is not
380875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        an entity
381875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        """.trim()
382875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    }
383875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
384875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    val FOREIGN_KEY_CANNOT_FIND_PARENT = "Cannot find parent entity class."
385875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
386875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    fun foreignKeyChildColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
387875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        return "($columnName) referenced in the foreign key does not exists in the Entity." +
388875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                " Available column names:${allColumns.joinToString(", ")}"
389875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    }
390875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
3916f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun foreignKeyParentColumnDoesNotExist(parentEntity: String,
392875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                                           missingColumn: String,
3936f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                                           allColumns: List<String>): String {
394875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        return "($missingColumn) does not exist in $parentEntity. Available columns are" +
395875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                " ${allColumns.joinToString(",")}"
396875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    }
397875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
398875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    val FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST = "Must specify at least 1 column name for the child"
399875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
400875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    val FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST = "Must specify at least 1 column name for the parent"
401875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
4026f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun foreignKeyColumnNumberMismatch(
4036f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            childColumns: List<String>, parentColumns: List<String>): String {
404875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        return """
405875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                Number of child columns in foreign key must match number of parent columns.
406875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                Child reference has ${childColumns.joinToString(",")} and parent reference has
407875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                ${parentColumns.joinToString(",")}
408875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar               """.trim()
409875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    }
410875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
4116f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun foreignKeyMissingParentEntityInDatabase(parentTable: String, childEntity: String): String {
412875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        return """
413875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                $parentTable table referenced in the foreign keys of $childEntity does not exist in
414875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                the database. Maybe you forgot to add the referenced entity in the entities list of
415875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                the @Database annotation?""".trim()
416875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    }
417875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
4186f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun foreignKeyMissingIndexInParent(parentEntity: String, parentColumns: List<String>,
4196f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas                                       childEntity: String, childColumns: List<String>): String {
420875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        return """
421875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                $childEntity has a foreign key (${childColumns.joinToString(",")}) that references
422875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                $parentEntity (${parentColumns.joinToString(",")}) but $parentEntity does not have
423875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                a unique index on those columns nor the columns are its primary key.
424875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                SQLite requires having a unique constraint on referenced parent columns so you must
425875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                add a unique index to $parentEntity that has
426875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                (${parentColumns.joinToString(",")}) column(s).
427875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar               """.trim()
428875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    }
429875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
4306f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun foreignKeyMissingIndexInChildColumns(childColumns: List<String>): String {
431875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        return """
432875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                (${childColumns.joinToString(",")}) column(s) reference a foreign key but
433875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                they are not part of an index. This may trigger full table scans whenever parent
434875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                table is modified so you are highly advised to create an index that covers these
435875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                columns.
436875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar               """.trim()
437875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    }
438875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar
4396f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun foreignKeyMissingIndexInChildColumn(childColumn: String): String {
440875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar        return """
441875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                $childColumn column references a foreign key but it is not part of an index. This
442875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                may trigger full table scans whenever parent table is modified so you are highly
443875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar                advised to create an index that covers this column.
444875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar               """.trim()
445875203d39f95be2367dec3ee70be4e2169b4e0f0Yigit Boyar    }
44608ddf77b7cc743f85b23e83de8b22b69fe4bf6d2Yigit Boyar
4476f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun shortcutEntityIsNotInDatabase(database: String, dao: String, entity: String): String {
44808ddf77b7cc743f85b23e83de8b22b69fe4bf6d2Yigit Boyar        return """
44908ddf77b7cc743f85b23e83de8b22b69fe4bf6d2Yigit Boyar                $dao is part of $database but this entity is not in the database. Maybe you forgot
45008ddf77b7cc743f85b23e83de8b22b69fe4bf6d2Yigit Boyar                to add $entity to the entities section of the @Database?
45108ddf77b7cc743f85b23e83de8b22b69fe4bf6d2Yigit Boyar                """.trim()
45208ddf77b7cc743f85b23e83de8b22b69fe4bf6d2Yigit Boyar    }
453f1ae0061c85cdb02bd124f8f8c7a934aa75e62feTyson Henning
454f1ae0061c85cdb02bd124f8f8c7a934aa75e62feTyson Henning    val MISSING_ROOM_GUAVA_ARTIFACT = "To use Guava features, you must add `guava`" +
455bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverette            " artifact from Room as a dependency. androidx.room:guava:<version>"
456f1ae0061c85cdb02bd124f8f8c7a934aa75e62feTyson Henning
4573c592c4ccbc6052b11443b0fa575052c08fefa55Yigit Boyar    val MISSING_ROOM_RXJAVA2_ARTIFACT = "To use RxJava2 features, you must add `rxjava2`" +
458bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverette            " artifact from Room as a dependency. androidx.room:rxjava2:<version>"
459f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar
4606f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun ambigiousConstructor(
4616f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas            pojo: String, paramName: String, matchingFields: List<String>): String {
462f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar        return """
463f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            Ambiguous constructor. The parameter ($paramName) in $pojo matches multiple fields:
464f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            [${matchingFields.joinToString(",")}]. If you don't want to use this constructor,
465f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            you can annotate it with @Ignore. If you want Room to use this constructor, you can
466f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            rename the parameters to exactly match the field name to fix the ambiguity.
467f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            """.trim()
468f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar    }
469f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar
470f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar    val MISSING_POJO_CONSTRUCTOR = """
471f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            Entities and Pojos must have a usable public constructor. You can have an empty
472f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            constructor or a constructor whose parameters match the fields (by name and type).
473f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            """.trim()
474f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar
475f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar    val TOO_MANY_POJO_CONSTRUCTORS = """
476f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            Room cannot pick a constructor since multiple constructors are suitable. Try to annotate
477f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            unwanted constructors with @Ignore.
478f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar            """.trim()
479abd098954d3fe996f336201ccb25884aaa34e07fYigit Boyar
480b9ea73472fb85e1a7074734824ad11c3f64cad83Yigit Boyar    val TOO_MANY_POJO_CONSTRUCTORS_CHOOSING_NO_ARG = """
481b9ea73472fb85e1a7074734824ad11c3f64cad83Yigit Boyar            There are multiple good constructors and Room will pick the no-arg constructor.
482b9ea73472fb85e1a7074734824ad11c3f64cad83Yigit Boyar            You can use the @Ignore annotation to eliminate unwanted constructors.
483b9ea73472fb85e1a7074734824ad11c3f64cad83Yigit Boyar            """.trim()
484b9ea73472fb85e1a7074734824ad11c3f64cad83Yigit Boyar
48537a4c4d3e29de0fa914c0496cafe0fca26de8bbfYigit Boyar    val RELATION_CANNOT_BE_CONSTRUCTOR_PARAMETER = """
48637a4c4d3e29de0fa914c0496cafe0fca26de8bbfYigit Boyar            Fields annotated with @Relation cannot be constructor parameters. These values are
48737a4c4d3e29de0fa914c0496cafe0fca26de8bbfYigit Boyar            fetched after the object is constructed.
48837a4c4d3e29de0fa914c0496cafe0fca26de8bbfYigit Boyar            """.trim()
48937a4c4d3e29de0fa914c0496cafe0fca26de8bbfYigit Boyar
490dc60cacc66972b47f27acdf727332878ce5958aeChris Craik    val PAGING_SPECIFY_DATA_SOURCE_TYPE = "For now, Room only supports PositionalDataSource class."
491540e3498175652abe452d8e0ed1c252e718ddf5aFlorina Muntenescu
4926f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas    fun primaryKeyNull(field: String): String {
4934c22afcd70f11a45e350b5729e67dc9f27deb319Florina Muntenescu        return "You must annotate primary keys with @NonNull. \"$field\" is nullable. SQLite " +
4944c22afcd70f11a45e350b5729e67dc9f27deb319Florina Muntenescu                "considers this a " +
4954c22afcd70f11a45e350b5729e67dc9f27deb319Florina Muntenescu                "bug and Room does not allow it. See SQLite docs for details: " +
4964c22afcd70f11a45e350b5729e67dc9f27deb319Florina Muntenescu                "https://www.sqlite.org/lang_createtable.html"
4974c22afcd70f11a45e350b5729e67dc9f27deb319Florina Muntenescu    }
498f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar
499f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar    val INVALID_COLUMN_NAME = "Invalid column name. Room does not allow using ` or \" in column" +
500f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar            " names"
501f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar
502f3b01d87d24552e0d716aa6b002fcd54e2522adfYigit Boyar    val INVALID_TABLE_NAME = "Invalid table name. Room does not allow using ` or \" in table names"
503de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar
504de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    val RAW_QUERY_BAD_PARAMS = "RawQuery methods should have 1 and only 1 parameter with type" +
505de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar            " String or SupportSQLiteQuery"
506de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar
507de23b91b2c982ef5c93349b16415654ae3fe5ac9Yigit Boyar    val RAW_QUERY_BAD_RETURN_TYPE = "RawQuery methods must return a non-void type."
50871d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar
50971d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar    fun rawQueryBadEntity(typeName: TypeName): String {
51071d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar        return """
51171d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar            observedEntities field in RawQuery must either reference a class that is annotated
51271d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar            with @Entity or it should reference a Pojo that either contains @Embedded fields that
51371d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar            are annotated with @Entity or @Relation fields.
51471d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar            $typeName does not have these properties, did you mean another class?
51571d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar            """.trim()
51671d73407ef17998685b29cc5c04cefb6d74e99c3Yigit Boyar    }
517809a35ac827a1cd7f3394dcff7254c1423739fc8Yigit Boyar
518809a35ac827a1cd7f3394dcff7254c1423739fc8Yigit Boyar    val RAW_QUERY_STRING_PARAMETER_REMOVED = "RawQuery does not allow passing a string anymore." +
519809a35ac827a1cd7f3394dcff7254c1423739fc8Yigit Boyar            " Please use ${SupportDbTypeNames.QUERY}."
52019b41105359a52aeb80070dec40247241231f05dYigit Boyar}
521