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();
  }