diff --git a/README.md b/README.md index 1973c85..3c06578 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ 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 `SQLiteUpgradeHelper` class - one `BaseColumns` interface per entity - one `Cursor` class per entity - one `ContentValues` class per entity @@ -20,13 +21,15 @@ This is where you declare a few parameters that will be used to generate the cod These are self-explanatory so here is an example: ```json { - "toolVersion": "1.5", + "toolVersion": "1.6", "projectPackageId": "com.example.app", "authority": "com.example.app.provider", "providerJavaPackage": "com.example.app.provider", "providerClassName": "ExampleProvider", - "sqliteHelperClassName": "ExampleSQLiteOpenHelper", + "sqliteOpenHelperClassName": "ExampleSQLiteOpenHelper", + "sqliteUpgradeHelperClassName": "ExampleSQLiteUpgradeHelper", "databaseFileName": "example.db", + "databaseVersion": 1, "enableForeignKeys": true, } ``` @@ -78,7 +81,7 @@ Here is a `person.json` file as an example: "enumValues": [ "MALE", "FEMALE", - "OTHER", + {"OTHER": "Value to use when neither male nor female"}, ], "nullable": false, }, @@ -106,7 +109,7 @@ https://github.com/BoD/android-contentprovider-generator/releases/latest ### Run the app -`java -jar android-contentprovider-generator-1.5-bundle.jar -i -o ` +`java -jar android-contentprovider-generator-1.6-bundle.jar -i -o ` - Input folder: where to find _config.json and your entity json files - Output folder: where the resulting files will be generated @@ -153,7 +156,7 @@ You need maven to build this app. `mvn package` -This will produce `android-contentprovider-generator-1.5-bundle.jar` in the `target` folder. +This will produce `android-contentprovider-generator-1.6-bundle.jar` in the `target` folder. Licence diff --git a/etc/sample/_config.json b/etc/sample/_config.json index c88df47..835c306 100644 --- a/etc/sample/_config.json +++ b/etc/sample/_config.json @@ -1,10 +1,12 @@ { - "toolVersion": "1.5", + "toolVersion": "1.6", "projectPackageId": "com.example.app", "authority": "com.example.app.provider", "providerJavaPackage": "com.example.app.provider", "providerClassName": "ExampleProvider", - "sqliteHelperClassName": "ExampleSQLiteOpenHelper", + "sqliteOpenHelperClassName": "ExampleSQLiteOpenHelper", + "sqliteUpgradeHelperClassName": "ExampleSQLiteUpgradeHelper", "databaseFileName": "example.db", + "databaseVersion": 1, "enableForeignKeys": true, } diff --git a/etc/sample/person.json b/etc/sample/person.json index 53fc901..f334220 100644 --- a/etc/sample/person.json +++ b/etc/sample/person.json @@ -31,7 +31,18 @@ "name": "company_id", "type": "Long", "nullable": false, - } + }, + { + "name": "gender", + "type": "enum", + "enumName": "Gender", + "enumValues": [ + "MALE", + "FEMALE", + {"OTHER": "Value to use when neither male nor female"}, + ], + "nullable": false, + }, ], "constraints": [ diff --git a/pom.xml b/pom.xml index dad3d2a..3c85e80 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.jraf android_contentprovider_generator - 1.5 + 1.6 jar GenerateAndroidProvider diff --git a/src/main/java/org/jraf/androidcontentprovidergenerator/Constants.java b/src/main/java/org/jraf/androidcontentprovidergenerator/Constants.java index 1d0fc6c..64c54f6 100644 --- a/src/main/java/org/jraf/androidcontentprovidergenerator/Constants.java +++ b/src/main/java/org/jraf/androidcontentprovidergenerator/Constants.java @@ -30,7 +30,7 @@ public class Constants { * Current version of this tool.
* Do not forget to update the value in the {@code pom.xml} file when updating this value. */ - public static final String VERSION = "1.5"; + public static final String VERSION = "1.6"; public static final String TAG = ""; } diff --git a/src/main/java/org/jraf/androidcontentprovidergenerator/Main.java b/src/main/java/org/jraf/androidcontentprovidergenerator/Main.java index 93bb05e..e9dbf15 100644 --- a/src/main/java/org/jraf/androidcontentprovidergenerator/Main.java +++ b/src/main/java/org/jraf/androidcontentprovidergenerator/Main.java @@ -40,6 +40,7 @@ import org.apache.commons.io.IOUtils; import org.jraf.androidcontentprovidergenerator.model.Constraint; import org.jraf.androidcontentprovidergenerator.model.Entity; +import org.jraf.androidcontentprovidergenerator.model.EnumValue; import org.jraf.androidcontentprovidergenerator.model.Field; import org.jraf.androidcontentprovidergenerator.model.Model; import org.json.JSONArray; @@ -63,9 +64,11 @@ public static class Json { public static final String PROJECT_PACKAGE_ID = "projectPackageId"; public static final String PROVIDER_JAVA_PACKAGE = "providerJavaPackage"; public static final String PROVIDER_CLASS_NAME = "providerClassName"; - public static final String SQLITE_HELPER_CLASS_NAME = "sqliteHelperClassName"; + public static final String SQLITE_OPEN_HELPER_CLASS_NAME = "sqliteOpenHelperClassName"; + public static final String SQLITE_UPGRADE_HELPER_CLASS_NAME = "sqliteUpgradeHelperClassName"; public static final String AUTHORITY = "authority"; public static final String DATABASE_FILE_NAME = "databaseFileName"; + public static final String DATABASE_VERSION = "databaseVersion"; public static final String ENABLE_FOREIGN_KEY = "enableForeignKeys"; } @@ -109,12 +112,21 @@ public boolean accept(File pathname) { String defaultValue = fieldJson.optString(Field.Json.DEFAULT_VALUE); String enumName = fieldJson.optString(Field.Json.ENUM_NAME); JSONArray enumValuesJson = fieldJson.optJSONArray(Field.Json.ENUM_VALUES); - List enumValues = new ArrayList(); + List enumValues = new ArrayList(); if (enumValuesJson != null) { int enumLen = enumValuesJson.length(); for (int j = 0; j < enumLen; j++) { - String valueName = enumValuesJson.getString(j); - enumValues.add(valueName); + Object enumValue = enumValuesJson.get(j); + if (enumValue instanceof String) { + // Name only + enumValues.add(new EnumValue((String) enumValue, null)); + } else { + // Name and Javadoc + JSONObject enumValueJson = (JSONObject) enumValue; + String enumValueName = (String) enumValueJson.keys().next(); + String enumValueJavadoc = enumValueJson.getString(enumValueName); + enumValues.add(new EnumValue(enumValueName, enumValueJavadoc)); + } } } Field field = new Field(name, type, isIndex, isNullable, defaultValue, enumName, enumValues); @@ -176,9 +188,11 @@ private void validateConfig() { ensureString(Json.PROJECT_PACKAGE_ID); ensureString(Json.PROVIDER_JAVA_PACKAGE); ensureString(Json.PROVIDER_CLASS_NAME); - ensureString(Json.SQLITE_HELPER_CLASS_NAME); + ensureString(Json.SQLITE_OPEN_HELPER_CLASS_NAME); + ensureString(Json.SQLITE_UPGRADE_HELPER_CLASS_NAME); ensureString(Json.AUTHORITY); ensureString(Json.DATABASE_FILE_NAME); + ensureInt(Json.DATABASE_VERSION); ensureBoolean(Json.ENABLE_FOREIGN_KEY); } @@ -198,6 +212,14 @@ private void ensureBoolean(String field) { } } + private void ensureInt(String field) { + try { + mConfig.getInt(field); + } catch (JSONException e) { + throw new IllegalArgumentException("Could not find '" + field + "' field in _config.json, which is mandatory and must be an int."); + } + } + private void generateColumns(Arguments arguments) throws IOException, JSONException, TemplateException { Template template = getFreeMarkerConfig().getTemplate("columns.ftl"); JSONObject config = getConfig(arguments.inputDir); @@ -315,13 +337,34 @@ private void generateContentProvider(Arguments arguments) throws IOException, JS template.process(root, out); } - private void generateSqliteHelper(Arguments arguments) throws IOException, JSONException, TemplateException { - Template template = getFreeMarkerConfig().getTemplate("sqlitehelper.ftl"); + private void generateSqliteOpenHelper(Arguments arguments) throws IOException, JSONException, TemplateException { + Template template = getFreeMarkerConfig().getTemplate("sqliteopenhelper.ftl"); JSONObject config = getConfig(arguments.inputDir); String providerJavaPackage = config.getString(Json.PROVIDER_JAVA_PACKAGE); File providerDir = new File(arguments.outputDir, providerJavaPackage.replace('.', '/')); providerDir.mkdirs(); - File outputFile = new File(providerDir, config.getString(Json.SQLITE_HELPER_CLASS_NAME) + ".java"); + File outputFile = new File(providerDir, config.getString(Json.SQLITE_OPEN_HELPER_CLASS_NAME) + ".java"); + Writer out = new OutputStreamWriter(new FileOutputStream(outputFile)); + + Map root = new HashMap(); + root.put("config", config); + root.put("model", Model.get()); + root.put("header", Model.get().getHeader()); + + template.process(root, out); + } + + private void generateSqliteUpgradeHelper(Arguments arguments) throws IOException, JSONException, TemplateException { + Template template = getFreeMarkerConfig().getTemplate("sqliteupgradehelper.ftl"); + JSONObject config = getConfig(arguments.inputDir); + String providerJavaPackage = config.getString(Json.PROVIDER_JAVA_PACKAGE); + File providerDir = new File(arguments.outputDir, providerJavaPackage.replace('.', '/')); + providerDir.mkdirs(); + File outputFile = new File(providerDir, config.getString(Json.SQLITE_UPGRADE_HELPER_CLASS_NAME) + ".java"); + if (outputFile.exists()) { + if (Config.LOGD) Log.d(TAG, "generateSqliteUpgradeHelper Upgrade helper class already exists: skip"); + return; + } Writer out = new OutputStreamWriter(new FileOutputStream(outputFile)); Map root = new HashMap(); @@ -348,7 +391,8 @@ private void go(String[] args) throws IOException, JSONException, TemplateExcept generateColumns(arguments); generateWrappers(arguments); generateContentProvider(arguments); - generateSqliteHelper(arguments); + generateSqliteOpenHelper(arguments); + generateSqliteUpgradeHelper(arguments); } public static void main(String[] args) throws Exception { diff --git a/src/main/java/org/jraf/androidcontentprovidergenerator/model/EnumValue.java b/src/main/java/org/jraf/androidcontentprovidergenerator/model/EnumValue.java new file mode 100644 index 0000000..45ee449 --- /dev/null +++ b/src/main/java/org/jraf/androidcontentprovidergenerator/model/EnumValue.java @@ -0,0 +1,43 @@ +/* + * This source is part of the + * _____ ___ ____ + * __ / / _ \/ _ | / __/___ _______ _ + * / // / , _/ __ |/ _/_/ _ \/ __/ _ `/ + * \___/_/|_/_/ |_/_/ (_)___/_/ \_, / + * /___/ + * repository. + * + * Copyright (C) 2012-2014 Benoit 'BoD' Lubek (BoD@JRAF.org) + * + * 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 . + */ +package org.jraf.androidcontentprovidergenerator.model; + +public class EnumValue { + private final String mName; + private final String mJavadoc; + + public EnumValue(String name, String javadoc) { + mName = name; + mJavadoc = javadoc; + } + + public String getName() { + return mName; + } + + public String getJavadoc() { + return mJavadoc; + } +} diff --git a/src/main/java/org/jraf/androidcontentprovidergenerator/model/Field.java b/src/main/java/org/jraf/androidcontentprovidergenerator/model/Field.java index 8fb6a30..3455696 100644 --- a/src/main/java/org/jraf/androidcontentprovidergenerator/model/Field.java +++ b/src/main/java/org/jraf/androidcontentprovidergenerator/model/Field.java @@ -109,9 +109,9 @@ public boolean hasNotNullableJavaType() { private final boolean mIsNullable; private final String mDefaultValue; private final String mEnumName; - private final List mEnumValues = new ArrayList(); + private final List mEnumValues = new ArrayList(); - public Field(String name, String type, boolean isIndex, boolean isNullable, String defaultValue, String enumName, List enumValues) { + public Field(String name, String type, boolean isIndex, boolean isNullable, String defaultValue, String enumName, List enumValues) { mName = name.toLowerCase(); mType = Type.fromJsonName(type); mIsIndex = isIndex; @@ -141,7 +141,7 @@ public String getEnumName() { return mEnumName; } - public List getEnumValues() { + public List getEnumValues() { return mEnumValues; } diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovider.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovider.ftl index 27ef447..b036e9d 100644 --- a/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovider.ftl +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovider.ftl @@ -54,11 +54,11 @@ public class ${config.providerClassName} extends ContentProvider { } - private ${config.sqliteHelperClassName} m${config.sqliteHelperClassName}; + private ${config.sqliteOpenHelperClassName} m${config.sqliteOpenHelperClassName}; @Override public boolean onCreate() { - m${config.sqliteHelperClassName} = ${config.sqliteHelperClassName}.newInstance(getContext()); + m${config.sqliteOpenHelperClassName} = ${config.sqliteOpenHelperClassName}.newInstance(getContext()); return true; } @@ -81,7 +81,7 @@ public class ${config.providerClassName} extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { if (BuildConfig.DEBUG) Log.d(TAG, "insert uri=" + uri + " values=" + values); final String table = uri.getLastPathSegment(); - final long rowId = m${config.sqliteHelperClassName}.getWritableDatabase().insert(table, null, values); + final long rowId = m${config.sqliteOpenHelperClassName}.getWritableDatabase().insert(table, null, values); String notify; if (rowId != -1 && ((notify = uri.getQueryParameter(QUERY_NOTIFY)) == null || "true".equals(notify))) { getContext().getContentResolver().notifyChange(uri, null); @@ -93,7 +93,7 @@ public class ${config.providerClassName} extends ContentProvider { public int bulkInsert(Uri uri, ContentValues[] values) { if (BuildConfig.DEBUG) Log.d(TAG, "bulkInsert uri=" + uri + " values.length=" + values.length); final String table = uri.getLastPathSegment(); - final SQLiteDatabase db = m${config.sqliteHelperClassName}.getWritableDatabase(); + final SQLiteDatabase db = m${config.sqliteOpenHelperClassName}.getWritableDatabase(); int res = 0; db.beginTransaction(); try { @@ -121,7 +121,7 @@ public class ${config.providerClassName} extends ContentProvider { if (BuildConfig.DEBUG) Log.d(TAG, "update uri=" + uri + " values=" + values + " selection=" + selection + " selectionArgs=" + Arrays.toString(selectionArgs)); final QueryParams queryParams = getQueryParams(uri, selection); - final int res = m${config.sqliteHelperClassName}.getWritableDatabase().update(queryParams.table, values, queryParams.selection, selectionArgs); + final int res = m${config.sqliteOpenHelperClassName}.getWritableDatabase().update(queryParams.table, values, queryParams.selection, selectionArgs); String notify; if (res != 0 && ((notify = uri.getQueryParameter(QUERY_NOTIFY)) == null || "true".equals(notify))) { getContext().getContentResolver().notifyChange(uri, null); @@ -133,7 +133,7 @@ public class ${config.providerClassName} extends ContentProvider { public int delete(Uri uri, String selection, String[] selectionArgs) { if (BuildConfig.DEBUG) Log.d(TAG, "delete uri=" + uri + " selection=" + selection + " selectionArgs=" + Arrays.toString(selectionArgs)); final QueryParams queryParams = getQueryParams(uri, selection); - final int res = m${config.sqliteHelperClassName}.getWritableDatabase().delete(queryParams.table, queryParams.selection, selectionArgs); + final int res = m${config.sqliteOpenHelperClassName}.getWritableDatabase().delete(queryParams.table, queryParams.selection, selectionArgs); String notify; if (res != 0 && ((notify = uri.getQueryParameter(QUERY_NOTIFY)) == null || "true".equals(notify))) { getContext().getContentResolver().notifyChange(uri, null); @@ -148,7 +148,7 @@ public class ${config.providerClassName} extends ContentProvider { Log.d(TAG, "query uri=" + uri + " selection=" + selection + " selectionArgs=" + Arrays.toString(selectionArgs) + " sortOrder=" + sortOrder + " groupBy=" + groupBy); final QueryParams queryParams = getQueryParams(uri, selection); - final Cursor res = m${config.sqliteHelperClassName}.getReadableDatabase().query(queryParams.table, projection, queryParams.selection, selectionArgs, groupBy, + final Cursor res = m${config.sqliteOpenHelperClassName}.getReadableDatabase().query(queryParams.table, projection, queryParams.selection, selectionArgs, groupBy, null, sortOrder == null ? queryParams.orderBy : sortOrder); res.setNotificationUri(getContext().getContentResolver(), uri); return res; @@ -156,7 +156,7 @@ public class ${config.providerClassName} extends ContentProvider { @Override public ContentProviderResult[] applyBatch(ArrayList operations) throws OperationApplicationException { - SQLiteDatabase db = m${config.sqliteHelperClassName}.getWritableDatabase(); + SQLiteDatabase db = m${config.sqliteOpenHelperClassName}.getWritableDatabase(); db.beginTransaction(); try { int numOperations = operations.size(); diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/enum.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/enum.ftl index d2daa35..95df9a6 100644 --- a/src/main/resources/org/jraf/androidcontentprovidergenerator/enum.ftl +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/enum.ftl @@ -7,9 +7,11 @@ package ${config.providerJavaPackage}.${entity.nameLowerCase}; * Possible values for the {@code ${field.nameLowerCase}} column of the {@code ${entity.nameLowerCase}} table. */ public enum ${field.enumName} { - // @formatter:off <#list field.enumValues as enumValue> - ${enumValue}, + /** + * ${enumValue.javadoc!""} + */ + ${enumValue.name}, + - // @formatter:on } \ No newline at end of file diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/sqlitehelper.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelper.ftl similarity index 75% rename from src/main/resources/org/jraf/androidcontentprovidergenerator/sqlitehelper.ftl rename to src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelper.ftl index 2b5a3bc..bbcf639 100644 --- a/src/main/resources/org/jraf/androidcontentprovidergenerator/sqlitehelper.ftl +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelper.ftl @@ -18,11 +18,11 @@ import ${config.projectPackageId}.BuildConfig; import ${config.providerJavaPackage}.${entity.nameLowerCase}.${entity.nameCamelCase}Columns; -public class ${config.sqliteHelperClassName} extends SQLiteOpenHelper { - private static final String TAG = ${config.sqliteHelperClassName}.class.getSimpleName(); +public class ${config.sqliteOpenHelperClassName} extends SQLiteOpenHelper { + private static final String TAG = ${config.sqliteOpenHelperClassName}.class.getSimpleName(); public static final String DATABASE_FILE_NAME = "${config.databaseFileName}"; - private static final int DATABASE_VERSION = 1; + private static final int DATABASE_VERSION = ${config.databaseVersion}; // @formatter:off <#list model.entities as entity> @@ -54,7 +54,7 @@ public class ${config.sqliteHelperClassName} extends SQLiteOpenHelper { // @formatter:on - public static ${config.sqliteHelperClassName} newInstance(Context context) { + public static ${config.sqliteOpenHelperClassName} newInstance(Context context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return newInstancePreHoneycomb(context); } @@ -66,11 +66,11 @@ public class ${config.sqliteHelperClassName} extends SQLiteOpenHelper { * Pre Honeycomb. */ - private static ${config.sqliteHelperClassName} newInstancePreHoneycomb(Context context) { - return new ${config.sqliteHelperClassName}(context, DATABASE_FILE_NAME, null, DATABASE_VERSION); + private static ${config.sqliteOpenHelperClassName} newInstancePreHoneycomb(Context context) { + return new ${config.sqliteOpenHelperClassName}(context, DATABASE_FILE_NAME, null, DATABASE_VERSION); } - private ${config.sqliteHelperClassName}(Context context, String name, CursorFactory factory, int version) { + private ${config.sqliteOpenHelperClassName}(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @@ -80,12 +80,12 @@ public class ${config.sqliteHelperClassName} extends SQLiteOpenHelper { */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private static ${config.sqliteHelperClassName} newInstancePostHoneycomb(Context context) { - return new ${config.sqliteHelperClassName}(context, DATABASE_FILE_NAME, null, DATABASE_VERSION, new DefaultDatabaseErrorHandler()); + private static ${config.sqliteOpenHelperClassName} newInstancePostHoneycomb(Context context) { + return new ${config.sqliteOpenHelperClassName}(context, DATABASE_FILE_NAME, null, DATABASE_VERSION, new DefaultDatabaseErrorHandler()); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private ${config.sqliteHelperClassName}(Context context, String name, CursorFactory factory, int version, DatabaseErrorHandler errorHandler) { + private ${config.sqliteOpenHelperClassName}(Context context, String name, CursorFactory factory, int version, DatabaseErrorHandler errorHandler) { super(context, name, factory, version, errorHandler); } @@ -115,6 +115,6 @@ public class ${config.sqliteHelperClassName} extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (BuildConfig.DEBUG) Log.d(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); + new ${config.sqliteUpgradeHelperClassName}().onUpgrade(db, oldVersion, newVersion); } } diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteupgradehelper.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteupgradehelper.ftl new file mode 100644 index 0000000..9fd4e3d --- /dev/null +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteupgradehelper.ftl @@ -0,0 +1,19 @@ +<#if header??> +${header} + +package ${config.providerJavaPackage}; + +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import ${config.projectPackageId}.BuildConfig; + +public class ${config.sqliteUpgradeHelperClassName} { + private static final String TAG = ${config.sqliteUpgradeHelperClassName}.class.getSimpleName(); + + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (BuildConfig.DEBUG) Log.d(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); + // Insert your upgrading code here. + // This file will not be overridden. + } +}