From c17ccecec53bfff9f250db515513992466a5828b Mon Sep 17 00:00:00 2001 From: Damyan Ivanov Date: Fri, 3 May 2019 19:07:19 +0300 Subject: [PATCH] drop a resource block around an SQLDatabase object hopefully this is the last such construct which may cause the global DB connection to be closed leading to a crash the next time it is used --- .../async/RetrieveTransactionsTask.java | 451 +++++++++--------- 1 file changed, 221 insertions(+), 230 deletions(-) diff --git a/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java b/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java index 1962173c..465977b3 100644 --- a/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java +++ b/app/src/main/java/net/ktnx/mobileledger/async/RetrieveTransactionsTask.java @@ -129,219 +129,212 @@ public class RetrieveTransactionsTask default: throw new HTTPException(http.getResponseCode(), http.getResponseMessage()); } - // FIXME: why the resource block here? that would close the global DB connection - try (SQLiteDatabase db = App.getDatabase()) { - try (InputStream resp = http.getInputStream()) { - if (http.getResponseCode() != 200) - throw new IOException(String.format("HTTP error %d", http.getResponseCode())); - db.beginTransaction(); - try { - prepareDbForRetrieval(db, profile); - - int matchedTransactionsCount = 0; - - - ParserState state = ParserState.EXPECTING_ACCOUNT; - String line; - BufferedReader buf = - new BufferedReader(new InputStreamReader(resp, StandardCharsets.UTF_8)); - - int processedTransactionCount = 0; - int transactionId = 0; - LedgerTransaction transaction = null; - LINES: - while ((line = buf.readLine()) != null) { - throwIfCancelled(); - Matcher m; - m = reComment.matcher(line); - if (m.find()) { - // TODO: comments are ignored for now -// Log.v("transaction-parser", "Ignoring comment"); - continue; - } - //L(String.format("State is %d", updating)); - switch (state) { - case EXPECTING_ACCOUNT: - if (line.equals("

General Journal

")) { - state = ParserState.EXPECTING_TRANSACTION; - L("→ expecting transaction"); - // commit the current transaction and start a new one - // the account list in the UI should reflect the (committed) - // state of the database - db.setTransactionSuccessful(); - db.endTransaction(); - Data.accounts.setList(accountList); - db.beginTransaction(); - continue; - } - m = reAccountName.matcher(line); - if (m.find()) { - String acct_encoded = m.group(1); - String acct_name = URLDecoder.decode(acct_encoded, "UTF-8"); - acct_name = acct_name.replace("\"", ""); - L(String.format("found account: %s", acct_name)); - - prevAccount = lastAccount; - lastAccount = profile.tryLoadAccount(db, acct_name); - if (lastAccount == null) - lastAccount = new LedgerAccount(acct_name); - else lastAccount.removeAmounts(); - profile.storeAccount(db, lastAccount); - - if (prevAccount != null) prevAccount - .setHasSubAccounts(prevAccount.isParentOf(lastAccount)); - // make sure the parent account(s) are present, - // synthesising them if necessary - // this happens when the (missing-in-HTML) parent account has - // only one child so we create a synthetic parent account record, - // copying the amounts when child's amounts are parsed - String parentName = lastAccount.getParentName(); - if (parentName != null) { - Stack toAppend = new Stack<>(); - while (parentName != null) { - if (accountNames.containsKey(parentName)) break; - toAppend.push(parentName); - parentName = - new LedgerAccount(parentName).getParentName(); - } - syntheticAccounts.clear(); - while (!toAppend.isEmpty()) { - String aName = toAppend.pop(); - LedgerAccount acc = profile.tryLoadAccount(db, aName); - if (acc == null) { - acc = new LedgerAccount(aName); - acc.setHiddenByStar(lastAccount.isHiddenByStar()); - acc.setExpanded(!lastAccount.hasSubAccounts() || - lastAccount.isExpanded()); - } - acc.setHasSubAccounts(true); - acc.removeAmounts(); // filled below when amounts are parsed - if ((!onlyStarred || !acc.isHiddenByStar()) && - acc.isVisible(accountList)) accountList.add(acc); - L(String.format("gap-filling with %s", aName)); - accountNames.put(aName, null); - profile.storeAccount(db, acc); - syntheticAccounts.put(aName, acc); - } - } - if ((!onlyStarred || !lastAccount.isHiddenByStar()) && - lastAccount.isVisible(accountList)) - accountList.add(lastAccount); - accountNames.put(acct_name, null); + SQLiteDatabase db = App.getDatabase(); + try (InputStream resp = http.getInputStream()) { + if (http.getResponseCode() != 200) + throw new IOException(String.format("HTTP error %d", http.getResponseCode())); + db.beginTransaction(); + try { + prepareDbForRetrieval(db, profile); - state = ParserState.EXPECTING_ACCOUNT_AMOUNT; - L("→ expecting account amount"); - } - break; + int matchedTransactionsCount = 0; - case EXPECTING_ACCOUNT_AMOUNT: - m = reAccountValue.matcher(line); - boolean match_found = false; - while (m.find()) { - throwIfCancelled(); - match_found = true; - String value = m.group(1); - String currency = m.group(2); - if (currency == null) currency = ""; - value = value.replace(',', '.'); - L("curr=" + currency + ", value=" + value); - final float val = Float.parseFloat(value); - profile.storeAccountValue(db, lastAccount.getName(), currency, - val); - lastAccount.addAmount(val, currency); - for (LedgerAccount syn : syntheticAccounts.values()) { - syn.addAmount(val, currency); - profile.storeAccountValue(db, syn.getName(), currency, - val); + ParserState state = ParserState.EXPECTING_ACCOUNT; + String line; + BufferedReader buf = + new BufferedReader(new InputStreamReader(resp, StandardCharsets.UTF_8)); + + int processedTransactionCount = 0; + int transactionId = 0; + LedgerTransaction transaction = null; + LINES: + while ((line = buf.readLine()) != null) { + throwIfCancelled(); + Matcher m; + m = reComment.matcher(line); + if (m.find()) { + // TODO: comments are ignored for now +// Log.v("transaction-parser", "Ignoring comment"); + continue; + } + //L(String.format("State is %d", updating)); + switch (state) { + case EXPECTING_ACCOUNT: + if (line.equals("

General Journal

")) { + state = ParserState.EXPECTING_TRANSACTION; + L("→ expecting transaction"); + // commit the current transaction and start a new one + // the account list in the UI should reflect the (committed) + // state of the database + db.setTransactionSuccessful(); + db.endTransaction(); + Data.accounts.setList(accountList); + db.beginTransaction(); + continue; + } + m = reAccountName.matcher(line); + if (m.find()) { + String acct_encoded = m.group(1); + String acct_name = URLDecoder.decode(acct_encoded, "UTF-8"); + acct_name = acct_name.replace("\"", ""); + L(String.format("found account: %s", acct_name)); + + prevAccount = lastAccount; + lastAccount = profile.tryLoadAccount(db, acct_name); + if (lastAccount == null) lastAccount = new LedgerAccount(acct_name); + else lastAccount.removeAmounts(); + profile.storeAccount(db, lastAccount); + + if (prevAccount != null) prevAccount + .setHasSubAccounts(prevAccount.isParentOf(lastAccount)); + // make sure the parent account(s) are present, + // synthesising them if necessary + // this happens when the (missing-in-HTML) parent account has + // only one child so we create a synthetic parent account record, + // copying the amounts when child's amounts are parsed + String parentName = lastAccount.getParentName(); + if (parentName != null) { + Stack toAppend = new Stack<>(); + while (parentName != null) { + if (accountNames.containsKey(parentName)) break; + toAppend.push(parentName); + parentName = new LedgerAccount(parentName).getParentName(); + } + syntheticAccounts.clear(); + while (!toAppend.isEmpty()) { + String aName = toAppend.pop(); + LedgerAccount acc = profile.tryLoadAccount(db, aName); + if (acc == null) { + acc = new LedgerAccount(aName); + acc.setHiddenByStar(lastAccount.isHiddenByStar()); + acc.setExpanded(!lastAccount.hasSubAccounts() || + lastAccount.isExpanded()); + } + acc.setHasSubAccounts(true); + acc.removeAmounts(); // filled below when amounts are parsed + if ((!onlyStarred || !acc.isHiddenByStar()) && + acc.isVisible(accountList)) accountList.add(acc); + L(String.format("gap-filling with %s", aName)); + accountNames.put(aName, null); + profile.storeAccount(db, acc); + syntheticAccounts.put(aName, acc); } } - if (match_found) { - state = ParserState.EXPECTING_ACCOUNT; - L("→ expecting account"); + if ((!onlyStarred || !lastAccount.isHiddenByStar()) && + lastAccount.isVisible(accountList)) + accountList.add(lastAccount); + accountNames.put(acct_name, null); + + state = ParserState.EXPECTING_ACCOUNT_AMOUNT; + L("→ expecting account amount"); + } + break; + + case EXPECTING_ACCOUNT_AMOUNT: + m = reAccountValue.matcher(line); + boolean match_found = false; + while (m.find()) { + throwIfCancelled(); + + match_found = true; + String value = m.group(1); + String currency = m.group(2); + if (currency == null) currency = ""; + value = value.replace(',', '.'); + L("curr=" + currency + ", value=" + value); + final float val = Float.parseFloat(value); + profile.storeAccountValue(db, lastAccount.getName(), currency, val); + lastAccount.addAmount(val, currency); + for (LedgerAccount syn : syntheticAccounts.values()) { + syn.addAmount(val, currency); + profile.storeAccountValue(db, syn.getName(), currency, val); } + } - break; + if (match_found) { + state = ParserState.EXPECTING_ACCOUNT; + L("→ expecting account"); + } - case EXPECTING_TRANSACTION: - if (!line.isEmpty() && (line.charAt(0) == ' ')) continue; - m = reTransactionStart.matcher(line); - if (m.find()) { - transactionId = Integer.valueOf(m.group(1)); - state = ParserState.EXPECTING_TRANSACTION_DESCRIPTION; - L(String.format(Locale.ENGLISH, - "found transaction %d → expecting description", - transactionId)); - progress.setProgress(++processedTransactionCount); - if (maxTransactionId < transactionId) - maxTransactionId = transactionId; - if ((progress.getTotal() == Progress.INDETERMINATE) || - (progress.getTotal() < transactionId)) - progress.setTotal(transactionId); - publishProgress(progress); + break; + + case EXPECTING_TRANSACTION: + if (!line.isEmpty() && (line.charAt(0) == ' ')) continue; + m = reTransactionStart.matcher(line); + if (m.find()) { + transactionId = Integer.valueOf(m.group(1)); + state = ParserState.EXPECTING_TRANSACTION_DESCRIPTION; + L(String.format(Locale.ENGLISH, + "found transaction %d → expecting description", + transactionId)); + progress.setProgress(++processedTransactionCount); + if (maxTransactionId < transactionId) + maxTransactionId = transactionId; + if ((progress.getTotal() == Progress.INDETERMINATE) || + (progress.getTotal() < transactionId)) + progress.setTotal(transactionId); + publishProgress(progress); + } + m = reEnd.matcher(line); + if (m.find()) { + L("--- transaction value complete ---"); + break LINES; + } + break; + + case EXPECTING_TRANSACTION_DESCRIPTION: + if (!line.isEmpty() && (line.charAt(0) == ' ')) continue; + m = reTransactionDescription.matcher(line); + if (m.find()) { + if (transactionId == 0) throw new TransactionParserException( + "Transaction Id is 0 while expecting " + "description"); + + String date = m.group(1); + try { + int equalsIndex = date.indexOf('='); + if (equalsIndex >= 0) date = date.substring(equalsIndex + 1); + transaction = + new LedgerTransaction(transactionId, date, m.group(2)); } - m = reEnd.matcher(line); - if (m.find()) { - L("--- transaction value complete ---"); - break LINES; + catch (ParseException e) { + e.printStackTrace(); + return String.format("Error parsing date '%s'", date); } - break; - - case EXPECTING_TRANSACTION_DESCRIPTION: - if (!line.isEmpty() && (line.charAt(0) == ' ')) continue; - m = reTransactionDescription.matcher(line); - if (m.find()) { - if (transactionId == 0) throw new TransactionParserException( - "Transaction Id is 0 while expecting " + "description"); - - String date = m.group(1); - try { - int equalsIndex = date.indexOf('='); - if (equalsIndex >= 0) - date = date.substring(equalsIndex + 1); - transaction = new LedgerTransaction(transactionId, date, - m.group(2)); - } - catch (ParseException e) { - e.printStackTrace(); - return String.format("Error parsing date '%s'", date); + state = ParserState.EXPECTING_TRANSACTION_DETAILS; + L(String.format(Locale.ENGLISH, + "transaction %d created for %s (%s) →" + + " expecting details", transactionId, date, m.group(2))); + } + break; + + case EXPECTING_TRANSACTION_DETAILS: + if (line.isEmpty()) { + // transaction data collected + if (transaction.existsInDb(db)) { + profile.markTransactionAsPresent(db, transaction); + matchedTransactionsCount++; + + if (matchedTransactionsCount == MATCHING_TRANSACTIONS_LIMIT) { + profile.markTransactionsBeforeTransactionAsPresent(db, + transaction); + progress.setTotal(progress.getProgress()); + publishProgress(progress); + break LINES; } - state = ParserState.EXPECTING_TRANSACTION_DETAILS; - L(String.format(Locale.ENGLISH, - "transaction %d created for %s (%s) →" + - " expecting details", transactionId, date, m.group(2))); } - break; - - case EXPECTING_TRANSACTION_DETAILS: - if (line.isEmpty()) { - // transaction data collected - if (transaction.existsInDb(db)) { - profile.markTransactionAsPresent(db, transaction); - matchedTransactionsCount++; - - if (matchedTransactionsCount == - MATCHING_TRANSACTIONS_LIMIT) - { - profile.markTransactionsBeforeTransactionAsPresent(db, - transaction); - progress.setTotal(progress.getProgress()); - publishProgress(progress); - break LINES; - } - } - else { - profile.storeTransaction(db, transaction); - matchedTransactionsCount = 0; - progress.setTotal(maxTransactionId); - } + else { + profile.storeTransaction(db, transaction); + matchedTransactionsCount = 0; + progress.setTotal(maxTransactionId); + } - state = ParserState.EXPECTING_TRANSACTION; - L(String.format("transaction %s saved → expecting transaction", - transaction.getId())); - transaction.finishLoading(); + state = ParserState.EXPECTING_TRANSACTION; + L(String.format("transaction %s saved → expecting transaction", + transaction.getId())); + transaction.finishLoading(); // sounds like a good idea, but transaction-1 may not be the first one chronologically // for example, when you add the initial seeding transaction after entering some others @@ -350,44 +343,42 @@ public class RetrieveTransactionsTask // "parser"); // break LINES; // } + } + else { + m = reTransactionDetails.matcher(line); + if (m.find()) { + String acc_name = m.group(1); + String amount = m.group(2); + String currency = m.group(3); + if (currency == null) currency = ""; + amount = amount.replace(',', '.'); + transaction.addAccount(new LedgerTransactionAccount(acc_name, + Float.valueOf(amount), currency)); + L(String.format(Locale.ENGLISH, "%d: %s = %s", + transaction.getId(), acc_name, amount)); } - else { - m = reTransactionDetails.matcher(line); - if (m.find()) { - String acc_name = m.group(1); - String amount = m.group(2); - String currency = m.group(3); - if (currency == null) currency = ""; - amount = amount.replace(',', '.'); - transaction.addAccount( - new LedgerTransactionAccount(acc_name, - Float.valueOf(amount), currency)); - L(String.format(Locale.ENGLISH, "%d: %s = %s", - transaction.getId(), acc_name, amount)); - } - else throw new IllegalStateException(String.format( - "Can't parse transaction %d " + "details: %s", - transactionId, line)); - } - break; - default: - throw new RuntimeException( - String.format("Unknown parser updating %s", state.name())); - } + else throw new IllegalStateException( + String.format("Can't parse transaction %d " + "details: %s", + transactionId, line)); + } + break; + default: + throw new RuntimeException( + String.format("Unknown parser updating %s", state.name())); } + } - throwIfCancelled(); + throwIfCancelled(); - profile.deleteNotPresentTransactions(db); - db.setTransactionSuccessful(); + profile.deleteNotPresentTransactions(db); + db.setTransactionSuccessful(); - profile.setLastUpdateStamp(); + profile.setLastUpdateStamp(); - return null; - } - finally { - db.endTransaction(); - } + return null; + } + finally { + db.endTransaction(); } } } -- 2.39.2