private void emitFields() throws IOException {
   writer.emitEmptyLine();
   writer.emitField(
       "String",
       "TAG",
       EnumSet.of(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL),
       "\"" + mModel.getContentProviderName() + "\"");
   writer.emitField(
       "String",
       "DATABASE_NAME",
       EnumSet.of(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL),
       "\"" + mModel.getDbClassName() + ".db\"");
   writer.emitField(
       "int",
       "DATABASE_VERSION",
       EnumSet.of(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL),
       mModel.getDbVersion());
   writer.emitField(
       "String",
       "ROW_ID",
       EnumSet.of(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL),
       "\"" + Table.ANDROID_ID + "\"");
   writer.emitEmptyLine();
   writer.emitField(
       "String",
       "AUTHORITY",
       EnumSet.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL),
       "\"" + mModel.getContentAuthority() + "\"");
   writer.emitEmptyLine();
   writer.emitField(mModel.getDbClassName(), "mLocalDatabase", EnumSet.of(Modifier.PRIVATE));
   writer.emitEmptyLine();
 }
  private void writeRemoveWith(Table table, Field field) throws IOException {
    ArrayList<String> params = new ArrayList<String>();
    params.add(SqlUtil.getJavaTypeFor(field.type));
    params.add(field.name);

    // remove with UNIQUE
    writer.emitEmptyLine();
    writer.beginMethod(
        "void",
        "remove" + Util.capitalize(table.name) + "With" + Util.capitalize(field.name),
        EnumSet.of(Modifier.PUBLIC),
        params.toArray(new String[params.size()]));
    writer.emitStatement(
        "ContentProviderOperation.Builder operationBuilder = ContentProviderOperation.newDelete("
            + mModel.getContentProviderName()
            + "."
            + SqlUtil.URI(table)
            + ")");
    writer.emitStatement(
        "operationBuilder.withSelection("
            + mModel.getDbClassName()
            + "."
            + SqlUtil.ROW_COLUMN(table, field)
            + " + \"=?\", new String[]{String.valueOf("
            + field.name
            + ")})");
    insertAddOpBlock();
    writer.endMethod();
  }
  private void emitTableCRUD(Table table) throws Exception {
    // Default array params for all rows
    ArrayList<String> params = new ArrayList<String>();
    for (Field row : table.fields) {

      params.add(SqlUtil.getJavaTypeFor(row.type));
      params.add(row.name);
    }

    ArrayList<String> paramsWithUnique = new ArrayList<String>();
    paramsWithUnique.add(SqlUtil.getJavaTypeFor(table.getPrimaryKey().type));
    paramsWithUnique.add(table.getPrimaryKey().name);

    ArrayList<String> updateParams = new ArrayList<String>();
    // TODO : add unique id
    updateParams.addAll(params);

    Iterator<Constraint> constraintiter;

    writer.emitEmptyLine();
    writer.emitJavadoc(
        table.name + " OPERATIONS\nall operations require this client to first run start");
    writer.emitEmptyLine();

    // Add through ContentProviderOperation
    writer.beginMethod(
        "void",
        "add" + Util.capitalize(table.name),
        EnumSet.of(Modifier.PUBLIC),
        params.toArray(new String[params.size()]));
    writer.emitStatement(
        "ContentProviderOperation.Builder operationBuilder = ContentProviderOperation.newInsert("
            + mModel.getContentProviderName()
            + "."
            + SqlUtil.URI(table)
            + ")");
    for (Field row : table.fields) {
      writer.emitStatement(
          "operationBuilder.withValue("
              + mModel.getDbClassName()
              + "."
              + SqlUtil.ROW_COLUMN(table, row)
              + ","
              + row.name
              + ")");
    }
    insertAddOpBlock();
    writer.endMethod();

    // Removes
    writeRemoveWith(table, table.getPrimaryKey());

    constraintiter = table.constraints.iterator();

    while (constraintiter.hasNext()) {
      Constraint constraint = constraintiter.next();
      if (constraint.type.equals(Constraint.Type.UNIQUE)) {
        final String[] fields = SqlUtil.getFieldsFromConstraint(constraint);
        for (int i = 0; i < fields.length; i++) {
          writeRemoveWith(table, table.getFieldByName(fields[i]));
        }
      }
    }

    // Remove All results
    writer.emitEmptyLine();
    writer.beginMethod(
        "void", "removeAll" + Util.capitalize(table.name), EnumSet.of(Modifier.PUBLIC));
    writer.emitStatement(
        "ContentProviderOperation.Builder operationBuilder = ContentProviderOperation.newDelete("
            + mModel.getContentProviderName()
            + "."
            + SqlUtil.URI(table)
            + ")");
    insertAddOpBlock();
    writer.endMethod();

    // Update through ContentProviderOperation
    writer.emitEmptyLine();
    writer.beginMethod(
        "void",
        "update" + Util.capitalize(table.name),
        EnumSet.of(Modifier.PUBLIC),
        updateParams.toArray(new String[updateParams.size()]));
    writer.emitStatement(
        "ContentProviderOperation.Builder operationBuilder = ContentProviderOperation.newUpdate("
            + mModel.getContentProviderName()
            + "."
            + SqlUtil.URI(table)
            + ")");
    for (Field row : table.fields) {
      writer.emitStatement(
          "operationBuilder.withValue("
              + mModel.getDbClassName()
              + "."
              + SqlUtil.ROW_COLUMN(table, row)
              + ","
              + row.name
              + ")");
    }
    insertAddOpBlock();
    writer.endMethod();
  }
  private void emitMethods() throws IOException {
    writer.emitEmptyLine();
    writer.emitAnnotation("Override");
    writer.beginMethod("boolean", "onCreate", EnumSet.of(Modifier.PUBLIC));
    writer.emitStatement("mLocalDatabase = new " + mModel.getDbClassName() + " (getContext())");
    writer.emitStatement("return true");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.beginMethod("String", "getTableNameFromUri", EnumSet.of(Modifier.PRIVATE), "Uri", "uri");
    writer.beginControlFlow("switch(uriMatcher.match(uri))");

    for (Table table : mModel.getTables()) {
      writer.emitStatement(
          "\tcase "
              + table.getAllName()
              + ":\ncase "
              + table.getSingleName()
              + ":\n\treturn "
              + SqlUtil.IDENTIFIER(table));
    }

    for (View view : mModel.getViews()) {
      writer.emitStatement("\tcase " + SqlUtil.IDENTIFIER(view) + ":\n\treturn " + view.name);
    }

    writer.emitStatement("default: break");
    writer.endControlFlow();
    writer.emitEmptyLine();
    writer.emitStatement("return null");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.beginMethod("String", "getUniqueKey", EnumSet.of(Modifier.PRIVATE), "Uri", "uri");
    writer.emitSingleLineComment("Only for actual tables");
    writer.beginControlFlow("switch(uriMatcher.match(uri))");

    for (Table table : mModel.getTables()) {
      writer.emitStatement(
          "\tcase "
              + table.getAllName()
              + ":\ncase "
              + table.getSingleName()
              + ":\n\treturn \""
              + table.UNIQUEROWID().snd
              + "\"");
    }

    writer.emitStatement("default: break");
    writer.endControlFlow();
    writer.emitEmptyLine();
    writer.emitStatement("return null");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.beginMethod(
        "boolean",
        "containsUnique",
        EnumSet.of(Modifier.PRIVATE),
        "Uri",
        "uri",
        "ContentValues",
        "contentvalues");
    writer.emitStatement("String unique = getUniqueKey(uri)");
    writer.beginControlFlow("for (String key : contentvalues.keySet())");
    writer.beginControlFlow("if (key.equals(unique))");
    writer.emitStatement("return true");
    writer.endControlFlow();
    writer.endControlFlow();
    writer.emitStatement("return false");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.beginMethod("Uri", "getContentUriFromUri", EnumSet.of(Modifier.PRIVATE), "Uri", "uri");
    writer.emitSingleLineComment("Only for actual tables");
    writer.beginControlFlow("switch(uriMatcher.match(uri))");

    for (Table table : mModel.getTables()) {
      writer.emitStatement(
          "\tcase "
              + table.getAllName()
              + ":\ncase "
              + table.getSingleName()
              + ":\n\treturn "
              + SqlUtil.URI(table));
    }

    writer.emitStatement("default: break");
    writer.endControlFlow();
    writer.emitEmptyLine();
    writer.emitStatement("return null");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.beginMethod(
        "ArrayList<Uri>", "getAssociatedViewUris", EnumSet.of(Modifier.PRIVATE), "Uri", "uri");
    writer.emitSingleLineComment("Only for actual views");
    writer.emitStatement("ArrayList<Uri> viewUris = null");
    writer.beginControlFlow("switch(uriMatcher.match(uri))");
    for (Table table : mModel.getTables()) {
      String statement =
          "\tcase " + table.getAllName() + ":\ncase " + table.getSingleName() + ":\n\t";
      ArrayList<String> associatedviews = new ArrayList<String>();
      for (View view : mModel.getViews()) {
        if (view.getFromtables().contains(table.name)) {
          associatedviews.add(SqlUtil.URI(view));
        }
      }

      if (!associatedviews.isEmpty()) {
        statement += "viewUris = new ArrayList<Uri>();\n\t";
      }

      for (String string : associatedviews) {
        statement += "viewUris.add(" + string + ");\n\t";
      }

      statement += "break";

      writer.emitStatement(statement);
    }

    writer.emitStatement("default: break");
    writer.endControlFlow();
    writer.emitEmptyLine();
    writer.emitStatement("return viewUris");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.emitAnnotation("Override");
    writer.beginMethod("String", "getType", EnumSet.of(Modifier.PUBLIC), "Uri", "uri");
    writer.emitSingleLineComment(
        "Return a string that identifies the MIME type for a Content Provider URI");
    writer.beginControlFlow("switch(uriMatcher.match(uri))");

    for (Table table : mModel.getTables()) {
      writer.emitStatement(
          "\tcase "
              + table.getAllName()
              + ":\n\treturn \"vnd.android.cursor.dir/vnd."
              + mModel.getClassPackage()
              + "."
              + table.name.toLowerCase()
              + "\"");
      writer.emitStatement(
          "\tcase "
              + table.getSingleName()
              + ":\n\treturn \"vnd.android.cursor.dir/vnd."
              + mModel.getClassPackage()
              + "."
              + table.name.toLowerCase()
              + "\"");
    }

    for (View view : mModel.getViews()) {
      writer.emitStatement(
          "\tcase "
              + SqlUtil.IDENTIFIER(view)
              + ":\n\treturn \"vnd.android.cursor.dir/vnd."
              + mModel.getClassPackage()
              + "."
              + view.name.toLowerCase()
              + "\"");
    }

    writer.emitStatement(
        "default:\n throw new IllegalArgumentException(\"Unsupported URI: \" + uri)");
    writer.endControlFlow();
    writer.endMethod();

    writer.emitEmptyLine();
    writer.emitAnnotation("Override");
    writer.beginMethod(
        "Cursor",
        "query",
        EnumSet.of(Modifier.PUBLIC),
        "Uri",
        "uri",
        "String[]",
        "projection",
        "String",
        "selection",
        "String[]",
        "selectionArgs",
        "String",
        "sortOrder");
    writer.emitSingleLineComment("Open database");
    writer.emitStatement("SQLiteDatabase db");
    writer.beginControlFlow("try");
    writer.emitStatement("db = mLocalDatabase.getWritableDatabase()");
    writer.nextControlFlow("catch (SQLiteException ex)");
    writer.emitStatement("db = mLocalDatabase.getReadableDatabase()");
    writer.endControlFlow();

    writer.emitSingleLineComment("Replace these with valid SQL statements if necessary.");
    writer.emitStatement("String groupBy = null");
    writer.emitStatement("String having = null");
    writer.emitStatement("SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder()");

    writer.emitSingleLineComment(
        "If this is a row query, limit the result set to the passed in row.");

    writer.emitStatement("String rowID");
    writer.beginControlFlow("switch(uriMatcher.match(uri))");

    for (Table table : mModel.getTables()) {
      writer.emitStatement(
          "case "
              + table.getSingleName()
              + ":\n\trowID = uri.getPathSegments().get(1);\n\tqueryBuilder.appendWhere(ROW_ID + \"=\" + rowID);\n\tbreak");
    }

    writer.emitStatement("default: break");
    writer.endControlFlow();

    writer.emitSingleLineComment(
        "Specify the table on which to perform the query. This can be a specific table or a join as required.");
    writer.emitStatement("queryBuilder.setTables(getTableNameFromUri(uri))");

    writer.emitEmptyLine();
    writer.emitSingleLineComment("Execute...");
    writer.emitStatement(
        "Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, groupBy, having, sortOrder)");
    writer.emitStatement("cursor.setNotificationUri(getContext().getContentResolver(), uri)");
    writer.emitStatement("return cursor");

    writer.endMethod();

    ArrayList<String> throwTypes = new ArrayList<String>();
    throwTypes.add("OperationApplicationException");
    ArrayList<String> parameters = new ArrayList<String>();
    parameters.add("ArrayList <ContentProviderOperation>");
    parameters.add("operations");

    writer.emitAnnotation("Override");
    writer.beginMethod(
        "ContentProviderResult[]",
        "applyBatch",
        EnumSet.of(Modifier.PUBLIC),
        parameters,
        throwTypes);
    writer.emitStatement("SQLiteDatabase db = mLocalDatabase.getWritableDatabase()");
    writer.emitStatement("db.beginTransaction()");
    writer.emitStatement("final int numOperations = operations.size()");
    writer.emitStatement(
        "final ContentProviderResult[] results = new ContentProviderResult[numOperations]");
    writer.emitStatement("Log.i(TAG, \"Applying a batch of \" + numOperations + \" operations.\")");
    writer.beginControlFlow("try");

    writer.beginControlFlow("for (int i = 0; i < numOperations; i++)");
    writer.emitStatement("results[i] = operations.get(i).apply(this, results, i)");
    writer.endControlFlow();
    writer.emitStatement("db.setTransactionSuccessful()");
    writer.nextControlFlow("finally");
    writer.emitStatement("db.endTransaction()");
    writer.endControlFlow();
    writer.emitStatement("return results");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.emitAnnotation("Override");
    writer.beginMethod(
        "int",
        "delete",
        EnumSet.of(Modifier.PUBLIC),
        "Uri",
        "uri",
        "String",
        "selection",
        "String[]",
        "selectionArgs");
    writer.emitSingleLineComment("Open database");
    writer.emitStatement("SQLiteDatabase db = mLocalDatabase.getWritableDatabase()");

    writer.emitStatement("String rowID");
    writer.emitStatement("String UNIQUEID");
    writer.beginControlFlow("switch(uriMatcher.match(uri))");

    for (Table table : mModel.getTables()) {
      writer.emitStatement(
          "\tcase "
              + table.getSingleName()
              + ":"
              + "\n\tUNIQUEID = \""
              + table.UNIQUEROWID().snd
              + "\";\n\trowID = uri.getPathSegments().get(1);\n\tselection = UNIQUEID + \"=\" + rowID + (!TextUtils.isEmpty(selection) ? \" AND (\" + selection + ')' : \"\")");
    }

    writer.emitStatement("default: break");
    writer.endControlFlow();

    writer.emitEmptyLine();
    writer.beginControlFlow("if (selection == null)");
    writer.emitStatement("selection = \"1\"");
    writer.endControlFlow();
    writer.emitEmptyLine();
    writer.emitStatement(
        "int deleteCount = db.delete(getTableNameFromUri(uri), selection, selectionArgs)");
    insertNotifyBlock();
    writer.emitEmptyLine();
    writer.emitStatement("return deleteCount");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.emitAnnotation("Override");
    writer.beginMethod(
        "Uri", "insert", EnumSet.of(Modifier.PUBLIC), "Uri", "uri", "ContentValues", "values");
    writer.emitSingleLineComment("Open database");
    writer.emitStatement("SQLiteDatabase db = mLocalDatabase.getWritableDatabase()");
    writer.emitSingleLineComment("Try to do an insert as per usual");
    writer.emitStatement("String nullColumnHack = null");
    writer.emitStatement("long id = db.insert(getTableNameFromUri(uri), nullColumnHack, values)");

    if (mModel.getConflictStrategy().equals("UPSERT")) {
      writer.emitEmptyLine();
      writer.beginControlFlow("if (id == -1)");
      writer.emitSingleLineComment("There was an error inserting, try upsert!");
      writer.beginControlFlow("if (containsUnique(uri, values))");
      writer.emitStatement("ContentValues withoutUnique = new ContentValues(values)");
      writer.emitStatement("String unique = getUniqueKey(uri)");
      writer.emitStatement("withoutUnique.remove(unique)");
      writer.emitStatement("String selection = unique + \"=?\"");
      writer.emitStatement(
          "String[] selectionArgs = new String[]{String.valueOf(values.get(unique))}");
      writer.emitStatement(
          "int updated = db.update(getTableNameFromUri(uri), withoutUnique, selection, selectionArgs)");
      writer.beginControlFlow("if (updated > 0)");
      writer.emitSingleLineComment(
          "If any row was updated, we'll get the row id from that row so we can pass it along below.");
      writer.emitStatement("SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder()");
      writer.emitStatement("queryBuilder.setTables(getTableNameFromUri(uri))");
      writer.emitStatement(
          "Cursor c = queryBuilder.query(db, new String[]{\"_id\"}, selection, selectionArgs, null, null, null)");
      writer.emitStatement("c.moveToNext()");
      writer.emitStatement("id = c.getLong(0)");
      writer.emitStatement("c.close()");
      writer.endControlFlow();
      writer.endControlFlow();
      writer.endControlFlow();
    }

    writer.emitEmptyLine();
    writer.beginControlFlow("if (id > -1)");
    writer.emitSingleLineComment("the insert was successful");
    writer.emitStatement(
        "Uri insertedId = ContentUris.withAppendedId(getContentUriFromUri(uri), id)");
    insertNotifyBlock();
    writer.emitEmptyLine();
    writer.emitStatement("return insertedId");
    writer.endControlFlow();
    writer.emitStatement("return null");
    writer.endMethod();

    writer.emitEmptyLine();
    writer.emitAnnotation("Override");
    writer.beginMethod(
        "int",
        "update",
        EnumSet.of(Modifier.PUBLIC),
        "Uri",
        "uri",
        "ContentValues",
        "values",
        "String",
        "selection",
        "String[]",
        "selectionArgs");
    writer.emitSingleLineComment("Open database");
    writer.emitStatement("SQLiteDatabase db = mLocalDatabase.getWritableDatabase()");
    writer.emitSingleLineComment("If this is a row URI, limit the deletion to the specified row.");

    writer.emitStatement("String rowID");
    writer.emitStatement("String UNIQUEID");
    writer.beginControlFlow("switch(uriMatcher.match(uri))");

    for (Table table : mModel.getTables()) {
      writer.emitStatement(
          "\tcase "
              + table.getSingleName()
              + ":"
              + "\n\tUNIQUEID = \""
              + table.UNIQUEROWID().snd
              + "\";\n\trowID = uri.getPathSegments().get(1);\n\tselection = UNIQUEID + \"=\" + rowID + (!TextUtils.isEmpty(selection) ? \" AND (\" + selection + ')' : \"\")");
    }
    writer.emitStatement("default: break");
    writer.endControlFlow();

    writer.emitSingleLineComment("Perform update");
    writer.emitStatement(
        "int updateCount = db.update(getTableNameFromUri(uri), values, selection, selectionArgs)");
    insertNotifyBlock();
    writer.emitEmptyLine();
    writer.emitStatement("return updateCount");
    writer.endMethod();
  }