#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
ContentProvider
class - a
SQLiteOpenHelper
class - a
SQLiteOpenHelperCallbacks
class - one
Columns
class per entity - one
Cursor
class per entity - one
ContentValues
class per entity - one
Selection
class per entity - one
Model
interface per entity
It generates the following extra code:
- basic implementation Model (Key and Value) objects based on their JSON definition
- a
ContentProviderDataSource
to be read/written asynchronously - a
SqliteDataSource
to access the database directly without going through the (slow) Content Provider - a
SqliteMapDataSource
similar to theSqliteDataSource
but reading Key/Value pairs from the data source. - the possibility to have no
_id
field at all when a Content Provider is not generated - provide a built-in
InvalidDbEntry
class to thow when bogus data are read from source(s)
- In
_config.json
asyncdbVersion
set to 1 for now to tell which extensions the JSON supports
- Entity JSON files
isKey
a boolean to set a field as part the key. An_id
field may still be generated but theisKey
fields will be used when looking for a particular element toupdate()
in the source.idField
can be set to an array of fields to define the map fields used as the keydataSources
is 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
_id
primary key field is automatically (implicitly) declared for all entities. It must not be declared in the json file. nullable
is optional (true by default).- if
documentation
is 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.json
and your entity json files - Output folder: where the resulting files will be generated
- When querying a table, use the corresponding
Selection
class 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
Cursor
in 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
ContentValues
class 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 KEY
SQL constraint is generated (ifenableForeignKeys
is set totrue
in_config.json
). - The
team
table will be automatically joined when querying theperson
table (only if anyteam
columns are included in the projection). - Getters for
team
columns are generated in thePersonCursor
wrapper. - Of course if
team
has foreign keys they will also be handled (and recursively).
- Foreign keys always reference the
_id
column (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
person
can 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
AS
keyword).
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!