#Android ContentProvider Generator
This is a fork of the original project to generate extra code to be used with Android InMemory/Async DB. It will generate the relevant data sources, element handlers and even CursorLoaders
A tool to generate an Android ContentProvider. It takes a set of entity (a.k.a "table") definitions as the input, and generates:
- a
ContentProviderclass - a
SQLiteOpenHelperclass - a
SQLiteOpenHelperCallbacksclass - one
Columnsclass per entity - one
Cursorclass per entity - one
ContentValuesclass per entity - one
Selectionclass per entity - one
Modelinterface per entity
It generates the following extra code:
- basic implementation Model (Key and Value) objects based on their JSON definition
- a
ContentProviderDataSourceto be read/written asynchronously - a
SqliteDataSourceto access the database directly without going through the (slow) Content Provider - a
SqliteMapDataSourcesimilar to theSqliteDataSourcebut reading Key/Value pairs from the data source. - the possibility to have no
_idfield at all when a Content Provider is not generated - provide a built-in
InvalidDbEntryclass to thow when bogus data are read from source(s)
- In
_config.jsonasyncdbVersionset to 1 for now to tell which extensions the JSON supports
- Entity JSON files
isKeya boolean to set a field as part the key. An_idfield may still be generated but theisKeyfields will be used when looking for a particular element toupdate()in the source.idFieldcan be set to an array of fields to define the map fields used as the keydataSourcesis an array with the list of output types that will be generated. It can be"ContentProvider"the regular CPG output"ContentProviderSource"a Content-Provider with all the extra classes to work with asyncdbContentProviderDataSource"SqliteDataSource"bare SQLite data source to work with asyncdbSqliteDataSource"SqliteMapDataSource"bare SQLite data source to work with asyncdbSqliteMapDataSource
This is where you declare a few parameters that will be used to generate the code.
These are self-explanatory so here is an example:
{
"syntaxVersion": 3,
"asyncdbVersion": 1,
"projectPackageId": "com.example.app",
"authority": "com.example.app.provider",
"providerJavaPackage": "com.example.app.provider",
"providerClassName": "ExampleProvider",
"sqliteOpenHelperClassName": "ExampleSQLiteOpenHelper",
"sqliteOpenHelperCallbacksClassName": "ExampleSQLiteOpenHelperCallbacks",
"databaseFileName": "example.db",
"databaseVersion": 1,
"enableForeignKeys": true,
"useAnnotations": true,
}Create one file per entity, naming it <entity_name>.json.
Inside each file, declare your fields (a.k.a "columns") with a name and a type.
You can also optionally declare a default value, an index flag, a documentation and a nullable flag.
Currently the type can be:
String(SQLite type:TEXT)Integer(INTEGER)Long(INTEGER)Float(REAL)Double(REAL)Boolean(INTEGER)Date(INTEGER)byte[](BLOB)enum(INTEGER).
You can also optionally declare table constraints.
Here is a person.json file as an example:
{
"documentation": "A human being which is part of a team.",
"fields": [
{
"documentation": "First name of this person. For instance, John.",
"name": "first_name",
"type": "String",
"defaultValue": "John",
"isKey": true,
},
{
"documentation": "Last name (a.k.a. Given name) of this person. For instance, Smith.",
"name": "last_name",
"type": "String",
"nullable": true,
"defaultValue": "Doe",
"isKey": true,
},
{
"name": "age",
"type": "Integer",
"index": true,
},
{
"name": "gender",
"type": "enum",
"enumName": "Gender",
"enumValues": [
"MALE",
"FEMALE",
{"OTHER": "Value to use when neither male nor female"},
],
"nullable": false,
},
],
"idField": ["first_name", "last_name"],
"dataSources": ["SqliteMapDataSource"],
}Notes:
- An
_idprimary key field is automatically (implicitly) declared for all entities. It must not be declared in the json file. nullableis optional (true by default).- if
documentationis present the value will be copied in Javadoc blocks in the generated code.
A more comprehensive example is available in the etc/sample folder.
You can also have a look at the corresponding generated code in the etc/sample/app folder.
By convention, your should name your entities and fields in lower case with words separated by '_', like in the example above.
If a header.txt file is present, its contents will be inserted at the top of every generated file.
Download the jar from here: https://github.com/BoD/android-contentprovider-generator/releases/latest
java -jar android-contentprovider-generator-1.9.0-bundle.jar -i <input folder> -o <output folder>
- Input folder: where to find
_config.jsonand your entity json files - Output folder: where the resulting files will be generated
- When querying a table, use the corresponding
Selectionclass as shown in this example:
PersonSelection where = new PersonSelection();
where.firstName("John").or().age(42);
Cursor c = context.getContentResolver().query(PersonColumns.CONTENT_URI, projection,
where.sel(), where.args(), null);- When using the results of a query, wrap the resulting
Cursorin the corresponding wrapper class. You can then use the generated getters directly as shown in this example:
PersonCursor person = new PersonCursor(c);
String lastName = person.getLastName();
Long age = person.getAge();- You can also conveniently combine these two facilities by using the
query(ordelete) method:
PersonSelection where = new PersonSelection();
where.firstName("John").or().age(42);
PersonCursor person = where.query(getContentResolver());
person.moveToNext();
String lastName = person.getLastName();
Long age = person.getAge();- When updating or inserting into a table, use the corresponding
ContentValuesclass as shown in this example:
PersonContentValues values = new PersonContentValues();
values.putFirstName("John").putAge(42);
context.getContentResolver().update(personUri, values.values(), null, null);There is limited support for foreign keys and joins. Here is an example of the syntax:
{
"fields": [
{
"name": "main_team_id",
"type": "Long",
"nullable": false,
"foreignKey": {
"table": "team",
"onDelete": "CASCADE",
},
},
{
"name": "first_name",
"type": "String",
"nullable": false,
},
(...)
}In this example, the field main_team_id is a foreign key referencing the primary key of the team table.
- The appropriate
FOREIGN KEYSQL constraint is generated (ifenableForeignKeysis set totruein_config.json). - The
teamtable will be automatically joined when querying thepersontable (only if anyteamcolumns are included in the projection). - Getters for
teamcolumns are generated in thePersonCursorwrapper. - Of course if
teamhas foreign keys they will also be handled (and recursively).
- Foreign keys always reference the
_idcolumn (the implicit primary key of all tables) and thus must always be of typeLong- by design. - Only one foreign key to a particular table is allowed per table. In the example above only one column in
personcan point toteam. - Loops (i.e. A has a foreign key to B and B has a foreign key to A) aren't detected. The generator will infinitely loop if they exist.
- Cases such as "A has a FK to B, B has a FK to C, A has a FK to C" generate ambiguities in the queries, because C columns appear twice. In the sample app you can see an example of how to deal with this case, using prefixes and aliases (SQL's
ASkeyword).
You need maven to build this tool.
mvn package
This will produce android-contentprovider-generator-1.9.0-bundle.jar in the target folder.
Here is a list of other tools that try to tackle the same problem.
I did not have the chance to try them out.
- https://github.com/SimonVT/schematic
- https://github.com/TimotheeJeannin/ProviGen
- http://triple-t.github.io/simpleprovider/
- https://github.com/foxykeep/ContentProviderCodeGenerator
- https://code.google.com/p/mdsd-android-content-provider/
- https://github.com/hamsterksu/Android-AnnotatedSQL
- http://robotoworks.com/mechanoid/doc/db/api.html
This program 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.
This program 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 this program. If not, see http://www.gnu.org/licenses/.
Just to be absolutely clear, this license applies to this program itself, not to the source it will generate!