/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorola.studio.android.codeutils.db.actions; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.datatools.modelbase.sql.tables.Column; import org.eclipse.datatools.modelbase.sql.tables.Table; import org.eclipse.emf.common.util.EList; import com.motorola.studio.android.common.exception.AndroidException; import com.motorola.studio.android.db.deployment.ContentProviderDeployer; import com.motorola.studio.android.db.deployment.ContentProviderDeployerByTable; import com.motorola.studio.android.db.deployment.DatabaseDeployer; /** * Helper class to create ContentProviders based on Sqlite tables. * It uses one file as template and substitutes parameters enclosed by hash-sign (#). * Warning: The tables named "ANDROID_METADATA" and "sqlite_sequence" will be ignored as it is an special table in Android apps context. */ public class ContentProviderGeneratorByTable extends AbstractCodeGeneratorByTable { private String dbName = null; /** * Creates a new content provider given a {@link org.eclipse.datatools.modelbase.sql.tables.Table Table} and the name of the database which the table is attached to. * @param table The table used in the content provider. * @param dbName The database name in which the table is attached to. * */ public ContentProviderGeneratorByTable(Table table, String dbName) { super(table); this.dbName = dbName; } /** * Creates content provider based on table from Sqlite * @param project * @param addCreateTableStatement NOT USED ON 1.3.0 (for future implementation) * @param addDropTableStatementOnUpdate NOT USED ON 1.3.0 (for future implementation) * @param overrideContentProviderIfExists If <code>true</code> overrides content provider in case it exists * @param contentProviderPackageName null to use default (.persistence inside project main package), otherwise the file path to place * @param databaseOpenHelperPackageName Database Open Helper package name * @param databaseOpenHelperClassName Database Open Helper class name * @throws IOException * @throws CoreException * @throws AndroidException */ public void createContentProvider(IProject project, boolean addCreateTableStatement, boolean addDropTableStatementOnUpdate, boolean overrideContentProviderIfExists, String contentProviderPackageName, String databaseOpenHelperPackageName, String databaseOpenHelperClassName, List<String> tableNameForClasses) throws IOException, CoreException, AndroidException { if (tableNameForClasses == null) { tableNameForClasses = new ArrayList<String>(); } if ((getTableName() != null) && !getTableName().equalsIgnoreCase(ANDROID_METADATA) && !getTableName().equalsIgnoreCase(SQLITE_SEQUENCES)) { String packageName = ""; if (contentProviderPackageName != null) { //use package defined by user packageName = contentProviderPackageName; } else { //use default package packageName = getPackageName(project); } String openHelperPackageName = ""; if (databaseOpenHelperPackageName != null) { // use package defined by user openHelperPackageName = databaseOpenHelperPackageName; } else { // use default package openHelperPackageName = getPackageName(project); } String contentProviderName = getJavaStyleTableName(tableNameForClasses) + "ContentProvider"; String authority = getAuthority(packageName, contentProviderName); //create parameters and copy content provider class to the android project Map<String, String> contentProviderParameters = new HashMap<String, String>(); contentProviderParameters.put(ContentProviderDeployer.ANDROID_PROJECT_PACKAGE_NAME, packageName); contentProviderParameters.put(ContentProviderDeployer.CONTENT_PROVIDER_CLASS_NAME, contentProviderName); contentProviderParameters.put(ContentProviderDeployer.CONTENT_PROVIDER_AUTHORITY, authority); contentProviderParameters.put("#databaseOpenHelperPackageName#", openHelperPackageName); contentProviderParameters.put("#databaseOpenHelperClassName#", databaseOpenHelperClassName); contentProviderParameters.put(DatabaseDeployer.DATABASE_NAME, getDbName()); contentProviderParameters.put("#tableName#", getTableName()); contentProviderParameters.put("#tableNameUpperCase#", getTableName().toUpperCase()); contentProviderParameters.put("#tableNameLowerCase#", getTableName().toLowerCase()); contentProviderParameters.put("#uriConstants#", getUriConstants()); contentProviderParameters.put("#constIndexesProjectMap#", getConstIndexesProjectMap()); contentProviderParameters.put("#constContentValuesKeys#", getConstContentValuesKeys()); contentProviderParameters.put("#defaultSortOrder#", getDefaultSortOrder()); contentProviderParameters.put("#queryUrlCases#", getQueryUrlCases()); contentProviderParameters.put("#typeRecordCases#", getTypeRecordCases(packageName)); contentProviderParameters.put("#insertStatementCases#", getInsertStatementCases()); contentProviderParameters.put("#deleteStatementCases#", getDeleteStatementCases()); contentProviderParameters.put("#updateStatementCases#", getUpdateStatementCases()); contentProviderParameters.put("#UrlMatcherStatementCases#", getUrlMatcherStatementCases()); contentProviderParameters.put("#ProjectionMapStatementCases#", getProjectionMapStatementCases()); ContentProviderDeployerByTable.copyContentProviderHelperClassToProject(project, contentProviderParameters, ContentProviderDeployerByTable.CONTENT_PROVIDER_BY_TABLE_CLASS_LOCATION, true, overrideContentProviderIfExists, new NullProgressMonitor()); } } /** * @param tableNameForClasses This list contains names that were used before and should not be used anymore * @return The table name in java style. */ private String getJavaStyleTableName(List<String> tableNameForClasses) { String originalTableName = getTableName(); //this loop will guarantee that the table name do not starts with _ while (originalTableName.charAt(0) == '_') { originalTableName = originalTableName.substring(1); } char[] charList = originalTableName.toLowerCase().toCharArray(); charList[0] = Character.toUpperCase(charList[0]); //will all character after _ in upper case, than remove all _ if (originalTableName.contains("_")) { for (int i = 1; i < charList.length; i++) { if (charList[i] == '_') { if ((i + 1) < charList.length) { charList[i + 1] = Character.toUpperCase(charList[i + 1]); } } } originalTableName = new String(charList); originalTableName = originalTableName.replace("_", ""); } else { originalTableName = new String(charList); } //will search tableNameForClasses to verify if there was a table with the same name //created before. If it was, we will put a counter in the end of the name. String newName = originalTableName; int count = 1; for (String name : tableNameForClasses) { if (name.equals(newName)) { count++; newName = originalTableName + count; } } tableNameForClasses.add(newName); return newName; } public String getDbName() { return dbName; } public void setDbName(String dbName) { this.dbName = dbName; } /** * @return The list of Uri constants to access items on query from Content Provider. */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getUriConstants() { StringBuilder buf = new StringBuilder(); String declaration = "public static final Uri "; Table table = getTable(); if (table != null) { EList columns = getTable().getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); String uriVarName = column.getName().toUpperCase() + "_FIELD_CONTENT_URI"; String parseStatement = ""; if (column.getName().equalsIgnoreCase("_ID")) { parseStatement = "Uri.parse(\"content://\"+AUTHORITY+\"/\"+TABLE_NAME.toLowerCase());"; } else { parseStatement = "Uri.parse(\"content://\"+AUTHORITY+\"/\"+TABLE_NAME.toLowerCase()+\"/" + column.getName().toLowerCase() + "\");"; } String uriDeclarationStatement = declaration + uriVarName + " = " + parseStatement; buf.append(uriDeclarationStatement + "\n"); } } } return buf.toString(); } /** * @return The list of indexes to access items on query from Content Provider. */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getConstIndexesProjectMap() { int initialIndex = 1; StringBuilder buf = new StringBuilder(); String declaration = "private static final int "; String tableUpperCase = getTableName().toUpperCase(); String constSt = declaration + tableUpperCase + " = " + initialIndex + ";"; buf.append(constSt + "\n"); initialIndex++; Table table = getTable(); if (table != null) { EList columns = getTable().getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); String constStByTable = declaration + tableUpperCase + "_" + column.getName().toUpperCase() + " = " + initialIndex + ";"; buf.append(constStByTable + "\n"); initialIndex++; } } } return buf.toString(); } /** * Default sort order will be set to the first primary key column. * If there is not, it will set as the first column name found. * @return java code to declare a sql string to order select results using the default column to sort the rows. */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getDefaultSortOrder() { Column firstColumn = null; Column chosenDefaultColumn = null; StringBuilder buf = new StringBuilder(); String declaration = "public static final String DEFAULT_SORT_ORDER = "; Table table = this.getTable(); if (table != null) { EList columns = table.getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { int index = columnsIter.nextIndex(); Column column = columnsIter.next(); if (index == 0) { //if no column is primary key, set it first column as default to sort firstColumn = column; } if (column.isPartOfPrimaryKey()) { chosenDefaultColumn = column; break; } } } } if (chosenDefaultColumn == null) { chosenDefaultColumn = firstColumn; } String defaultSortSt = declaration + "\"" + chosenDefaultColumn.getName() + " ASC\";"; buf.append(defaultSortSt + "\n"); return buf.toString(); } /** * @return The URLs to be used on queries. */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getQueryUrlCases() { StringBuilder buf = new StringBuilder(); String caseSt = "case " + getTableName().toUpperCase() + ":"; buf.append(caseSt + "\n"); String setTablesSt = "qb.setTables(TABLE_NAME);"; String setProjectionSt = "qb.setProjectionMap(" + getTableName().toUpperCase() + "_PROJECTION_MAP);"; String breakSt = "break;"; buf.append("\t" + setTablesSt + "\n"); buf.append("\t" + setProjectionSt + "\n"); buf.append("\t" + breakSt + "\n"); Table table = getTable(); if (table != null) { EList columns = getTable().getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); String caseColumnSt = "case " + getTableName().toUpperCase() + "_" + column.getName().toUpperCase() + ":"; buf.append(caseColumnSt + "\n"); String setTablesColumnsSt = "qb.setTables(TABLE_NAME);"; buf.append("\t" + setTablesColumnsSt + "\n"); String setAppendWhereSt = ""; if (column.getName().equalsIgnoreCase("_ID")) { //obrigatory primary key on android setAppendWhereSt = "qb.appendWhere(\"" + column.getName().toLowerCase() + "=\" + url.getPathSegments().get(1));"; } else { setAppendWhereSt = "qb.appendWhere(\"" + column.getName().toLowerCase() + "='\" + url.getPathSegments().get(2)+\"'\");"; } buf.append("\t" + setAppendWhereSt + "\n"); buf.append("\t" + breakSt + "\n"); } } } return buf.toString(); } /** * Get types for multiple (records for the entire Bean - use dir cursor) * or single items (simple columns into the Bean - use item cursor) */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getTypeRecordCases(String packageName) { StringBuilder buf = new StringBuilder(); String caseSt = "case " + getTableName().toUpperCase() + ":"; buf.append(caseSt + "\n"); String returnMultipleSt = "return " + "\"vnd.android.cursor.dir/vnd." + packageName + "." + getTableName().toLowerCase() + "\"" + ";"; buf.append("\t" + returnMultipleSt + "\n"); Table table = getTable(); if (table != null) { EList columns = getTable().getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); String caseColumnSt = "case " + getTableName().toUpperCase() + "_" + column.getName().toUpperCase() + ":"; buf.append(caseColumnSt + "\n"); String returnSingleSt = "return " + "\"vnd.android.cursor.item/vnd." + packageName + "." + getTableName().toLowerCase() + "\"" + ";"; buf.append("\t" + returnSingleSt + "\n"); } } } return buf.toString(); } /** * @return The insert statements generated based on table columns. */ private String getInsertStatementCases() { //Empty for now - Let method because users can ask something in the future return ""; } /** * * @return The delete statements generated based on table columns. */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getDeleteStatementCases() { StringBuilder buf = new StringBuilder(); String caseSt = "case " + getTableName().toUpperCase() + ":"; buf.append(caseSt + "\n"); String countSt = "count = mDB.delete(TABLE_NAME, where, whereArgs);"; buf.append("\t" + countSt + "\n"); buf.append("\t" + "break;" + "\n"); Table table = getTable(); if (table != null) { EList columns = getTable().getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); String caseColumnSt = "case " + getTableName().toUpperCase() + "_" + column.getName().toUpperCase() + ":"; buf.append(caseColumnSt + "\n"); String segmentSt = ""; if (column.getName().equalsIgnoreCase("_ID")) { segmentSt = "segment = url.getPathSegments().get(1);"; } else { //non-id items segmentSt = "segment = \"'\" + url.getPathSegments().get(2) + \"'\";"; } buf.append("\t" + segmentSt + "\n"); String countStByColumn = "count = mDB.delete(TABLE_NAME, \"" + column.getName().toLowerCase() + "=\" + segment + (!TextUtils.isEmpty(where) ? \" AND (\" + where + ')' : \"\"), whereArgs);"; buf.append("\t" + countStByColumn + "\n"); buf.append("\t" + "break;" + "\n"); } } } return buf.toString(); } /** * @return Update statements generated based on table columns. */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getUpdateStatementCases() { StringBuilder buf = new StringBuilder(); String caseSt = "case " + getTableName().toUpperCase() + ":"; buf.append(caseSt + "\n"); String countSt = "count = mDB.update(TABLE_NAME, values, where, whereArgs);"; buf.append("\t" + countSt + "\n"); buf.append("\t" + "break;" + "\n"); Table table = getTable(); if (table != null) { EList columns = getTable().getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); String caseColumnSt = "case " + getTableName().toUpperCase() + "_" + column.getName().toUpperCase() + ":"; buf.append(caseColumnSt + "\n"); String segmentSt = ""; if (column.getName().equalsIgnoreCase("_ID")) { segmentSt = "segment = url.getPathSegments().get(1);"; } else { //non-id items segmentSt = "segment = \"'\" + url.getPathSegments().get(2) + \"'\";"; } buf.append("\t" + segmentSt + "\n"); String countStByColumn = "count = mDB.update(TABLE_NAME, values, \"" + column.getName().toLowerCase() + "=\" + segment + (!TextUtils.isEmpty(where) ? \" AND (\" + where + ')' : \"\"), whereArgs);"; buf.append("\t" + countStByColumn + "\n"); buf.append("\t" + "break;" + "\n"); } } } return buf.toString(); } /** * @return The matcher to recognize the pattern developer is trying to query the data from ContentProvider. */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getUrlMatcherStatementCases() { StringBuilder buf = new StringBuilder(); buf.append("URL_MATCHER.addURI(AUTHORITY, TABLE_NAME.toLowerCase(), " + getTableName().toUpperCase() + ");" + "\n"); Table table = getTable(); if (table != null) { EList columns = getTable().getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); if (column.getName().equalsIgnoreCase("_ID")) { buf.append("URL_MATCHER.addURI(AUTHORITY, TABLE_NAME.toLowerCase()" + "+\"/#\", " + getTableName().toUpperCase() + "_" + column.getName().toUpperCase() + ");" + "\n"); } else { //non-id items buf.append("URL_MATCHER.addURI(AUTHORITY, TABLE_NAME.toLowerCase()" + "+\"/" + column.getName().toLowerCase() + "\"+\"/*\", " + getTableName().toUpperCase() + "_" + column.getName().toUpperCase() + ");" + "\n"); } } } } return buf.toString(); } @SuppressWarnings( { "rawtypes", "unchecked" }) private String getConstContentValuesKeys() { StringBuilder buf = new StringBuilder(); String declaration = "\tpublic static final String "; Table table = this.getTable(); if (table != null) { EList columns = table.getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); String contValuesKeysSt = declaration + column.getName().toUpperCase() + " = \"" + column.getName() + "\";"; buf.append(contValuesKeysSt + "\n"); } } } return buf.toString(); } /** * Initialize maps to get table items into the static constructor from the Provider. */ @SuppressWarnings( { "unchecked", "rawtypes" }) private String getProjectionMapStatementCases() { StringBuilder buf = new StringBuilder(); Table table = getTable(); if (table != null) { EList columns = getTable().getColumns(); if ((columns != null) && (columns.size() > 0)) { ListIterator<Column> columnsIter = columns.listIterator(); while (columnsIter.hasNext()) { Column column = columnsIter.next(); buf.append(getTableName().toUpperCase() + "_PROJECTION_MAP.put(" + column.getName().toUpperCase() + "," + "\"" + column.getName().toLowerCase() + "\");" + "\n"); } } } return buf.toString(); } }