package com.code44.finance.data.backup;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import com.code44.finance.common.model.DecimalSeparator;
import com.code44.finance.common.model.GroupSeparator;
import com.code44.finance.common.model.ModelState;
import com.code44.finance.common.model.SymbolPosition;
import com.code44.finance.common.model.TransactionState;
import com.code44.finance.common.model.TransactionType;
import com.code44.finance.data.DataStore;
import com.code44.finance.data.db.DBHelper;
import com.code44.finance.data.db.Tables;
import com.code44.finance.data.model.Account;
import com.code44.finance.data.model.Category;
import com.code44.finance.data.model.Currency;
import com.code44.finance.data.model.Model;
import com.code44.finance.data.model.SyncState;
import com.code44.finance.data.model.Tag;
import com.code44.finance.data.model.Transaction;
import com.code44.finance.data.providers.AccountsProvider;
import com.code44.finance.data.providers.CategoriesProvider;
import com.code44.finance.data.providers.CurrenciesProvider;
import com.code44.finance.data.providers.TagsProvider;
import com.code44.finance.data.providers.TransactionsProvider;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class BackupDataImporter extends DataImporter {
private static final int MIN_VALID_VERSION = 7;
private final Context context;
private final DBHelper dbHelper;
private final boolean merge;
public BackupDataImporter(InputStream inputStream, Context context, DBHelper dbHelper, boolean merge) {
super(inputStream);
this.context = context.getApplicationContext();
this.dbHelper = dbHelper;
this.merge = merge;
}
@Override protected void importData(InputStream inputStream) throws Exception {
final JsonObject json = inputStreamToJson(inputStream);
validate(json);
final SQLiteDatabase database = dbHelper.getWritableDatabase();
try {
database.beginTransaction();
if (!merge) {
cleanDatabase(database);
}
importCurrencies(json);
importCategories(json);
importTags(json);
importAccounts(json);
importTransactions(json);
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}
private JsonObject inputStreamToJson(InputStream inputStream) {
final JsonParser parser = new JsonParser();
final JsonElement jsonElement = parser.parse(new InputStreamReader(inputStream));
return jsonElement.getAsJsonObject();
}
private void validate(JsonObject json) {
final int version = json.get("version").getAsInt();
if (version < MIN_VALID_VERSION) {
throw new IllegalArgumentException("Backup version " + version + " is not supported anymore.");
}
}
private void cleanDatabase(SQLiteDatabase database) {
database.delete(Tables.Currencies.TABLE_NAME, null, null);
database.delete(Tables.Categories.TABLE_NAME, null, null);
database.delete(Tables.Tags.TABLE_NAME, null, null);
database.delete(Tables.Accounts.TABLE_NAME, null, null);
database.delete(Tables.Transactions.TABLE_NAME, null, null);
database.delete(Tables.TransactionTags.TABLE_NAME, null, null);
}
private void importCurrencies(JsonObject json) {
final List<ContentValues> valuesList = new ArrayList<>();
final JsonArray modelsJson = json.getAsJsonArray("currencies");
final Currency model = new Currency();
for (int i = 0, size = modelsJson.size(); i < size; i++) {
final JsonObject modelJson = modelsJson.get(i).getAsJsonObject();
updateBaseModel(model, modelJson);
model.setCode(modelJson.get("code").getAsString());
model.setSymbol(modelJson.get("symbol").getAsString());
model.setSymbolPosition(SymbolPosition.fromInt(modelJson.get("symbol_position").getAsInt()));
model.setDecimalSeparator(DecimalSeparator.fromSymbol(modelJson.get("decimal_separator").getAsString()));
model.setGroupSeparator(GroupSeparator.fromSymbol(modelJson.get("group_separator").getAsString()));
model.setDecimalCount(modelJson.get("decimal_count").getAsInt());
model.setDefault(modelJson.get("is_default").getAsBoolean());
model.setExchangeRate(modelJson.get("exchange_rate").getAsDouble());
valuesList.add(model.asValues());
}
insert(valuesList, CurrenciesProvider.uriCurrencies());
}
private void importCategories(JsonObject json) {
final List<ContentValues> valuesList = new ArrayList<>();
final JsonArray modelsJson = json.getAsJsonArray("categories");
final Category model = new Category();
for (int i = 0, size = modelsJson.size(); i < size; i++) {
final JsonObject modelJson = modelsJson.get(i).getAsJsonObject();
updateBaseModel(model, modelJson);
model.setTitle(modelJson.get("title").getAsString());
model.setColor(modelJson.get("color").getAsInt());
model.setTransactionType(TransactionType.fromInt(modelJson.get("transaction_type").getAsInt()));
model.setSortOrder(modelJson.get("sort_order").getAsInt());
valuesList.add(model.asValues());
}
insert(valuesList, CategoriesProvider.uriCategories());
}
private void importTags(JsonObject json) {
final List<ContentValues> valuesList = new ArrayList<>();
final JsonArray modelsJson = json.getAsJsonArray("tags");
final Tag model = new Tag();
for (int i = 0, size = modelsJson.size(); i < size; i++) {
final JsonObject modelJson = modelsJson.get(i).getAsJsonObject();
updateBaseModel(model, modelJson);
model.setTitle(modelJson.get("title").getAsString());
valuesList.add(model.asValues());
}
insert(valuesList, TagsProvider.uriTags());
}
private void importAccounts(JsonObject json) {
final List<ContentValues> valuesList = new ArrayList<>();
final JsonArray modelsJson = json.getAsJsonArray("accounts");
final Account model = new Account();
final Currency currency = new Currency();
model.setCurrency(currency);
for (int i = 0, size = modelsJson.size(); i < size; i++) {
final JsonObject modelJson = modelsJson.get(i).getAsJsonObject();
updateBaseModel(model, modelJson);
currency.setId(modelJson.get("currency_id").getAsString());
model.setTitle(modelJson.get("title").getAsString());
model.setNote(modelJson.get("note").getAsString());
model.setIncludeInTotals(modelJson.get("include_in_totals").getAsBoolean());
valuesList.add(model.asValues());
}
insert(valuesList, AccountsProvider.uriAccounts());
}
private void importTransactions(JsonObject json) {
final List<ContentValues> valuesList = new ArrayList<>();
final JsonArray modelsJson = json.getAsJsonArray("transactions");
final Transaction model = new Transaction();
final Account accountFrom = new Account();
final Account accountTo = new Account();
final Category category = new Category();
final List<Tag> tags = new ArrayList<>();
final Set<Tag> tagCache = new HashSet<>();
for (int i = 0, size = modelsJson.size(); i < size; i++) {
model.setAccountFrom(accountFrom);
model.setAccountTo(accountTo);
model.setCategory(category);
model.setTags(tags);
final JsonObject modelJson = modelsJson.get(i).getAsJsonObject();
updateBaseModel(model, modelJson);
accountFrom.setId(modelJson.get("account_from_id").isJsonNull() ? null : modelJson.get("account_from_id").getAsString());
accountTo.setId(modelJson.get("account_to_id").isJsonNull() ? null : modelJson.get("account_to_id").getAsString());
category.setId(modelJson.get("category_id").isJsonNull() ? null : modelJson.get("category_id").getAsString());
tagCache.addAll(tags);
tags.clear();
final JsonArray tagsJson = modelJson.get("tag_ids").getAsJsonArray();
for (int tagI = 0, tagSize = tagsJson.size(); tagI < tagSize; tagI++) {
final Tag tag = getTagInstance(tagCache);
tag.setId(tagsJson.get(tagI).getAsString());
tags.add(tag);
}
model.setDate(modelJson.get("date").getAsLong());
model.setAmount(modelJson.get("amount").getAsLong());
model.setExchangeRate(modelJson.get("exchange_rate").getAsDouble());
model.setNote(modelJson.get("note").getAsString());
model.setTransactionState(TransactionState.fromInt(modelJson.get("transaction_state").getAsInt()));
model.setTransactionType(TransactionType.fromInt(modelJson.get("transaction_type").getAsInt()));
model.setIncludeInReports(modelJson.get("include_in_reports").getAsBoolean());
valuesList.add(model.asValues());
}
insert(valuesList, TransactionsProvider.uriTransactions());
}
private Tag getTagInstance(Set<Tag> tagCache) {
if (tagCache.size() > 0) {
final Iterator<Tag> iterator = tagCache.iterator();
final Tag tag = iterator.next();
iterator.remove();
return tag;
} else {
return new Tag();
}
}
private void updateBaseModel(Model model, JsonObject json) {
model.setId(json.get("id").getAsString());
model.setModelState(ModelState.fromInt(json.get("model_state").getAsInt()));
model.setSyncState(SyncState.fromInt(json.get("sync_state").getAsInt()));
}
private void insert(List<ContentValues> valuesList, Uri uri) {
if (valuesList.size() > 0) {
DataStore.bulkInsert().values(valuesList).into(context, uri);
}
}
}