private void init(@Nullable SQLiteDatabase db) {
    if (db == null) {
      mAccountsDbAdapter = AccountsDbAdapter.getInstance();
      mTransactionsDbAdapter = TransactionsDbAdapter.getInstance();
      mScheduledActionsDbAdapter = ScheduledActionDbAdapter.getInstance();
      mCommoditiesDbAdapter = CommoditiesDbAdapter.getInstance();
      mPricesDbAdapter = PricesDbAdapter.getInstance();
    } else {
      mTransactionsDbAdapter = new TransactionsDbAdapter(db, new SplitsDbAdapter(db));
      mAccountsDbAdapter = new AccountsDbAdapter(db, mTransactionsDbAdapter);
      mScheduledActionsDbAdapter = new ScheduledActionDbAdapter(db);
      mCommoditiesDbAdapter = new CommoditiesDbAdapter(db);
      mPricesDbAdapter = new PricesDbAdapter(db);
    }

    mContent = new StringBuilder();

    mAccountList = new ArrayList<>();
    mAccountMap = new HashMap<>();
    mTransactionList = new ArrayList<>();
    mScheduledActionsList = new ArrayList<>();

    mTemplatAccountList = new ArrayList<>();
    mTemplateTransactions = new ArrayList<>();
    mTemplateAccountToTransactionMap = new HashMap<>();

    mAutoBalanceSplits = new ArrayList<>();

    mPriceList = new ArrayList<>();
  }
  @Override
  public void endDocument() throws SAXException {
    super.endDocument();
    HashMap<String, String> mapFullName = new HashMap<>(mAccountList.size());
    HashMap<String, Account> mapImbalanceAccount = new HashMap<>();

    // The XML has no ROOT, create one
    if (mRootAccount == null) {
      mRootAccount = new Account("ROOT");
      mRootAccount.setAccountType(AccountType.ROOT);
      mAccountList.add(mRootAccount);
      mAccountMap.put(mRootAccount.getUID(), mRootAccount);
    }

    String imbalancePrefix = AccountsDbAdapter.getImbalanceAccountPrefix();

    // Add all account without a parent to ROOT, and collect top level imbalance accounts
    for (Account account : mAccountList) {
      mapFullName.put(account.getUID(), null);
      boolean topLevel = false;
      if (account.getParentUID() == null && account.getAccountType() != AccountType.ROOT) {
        account.setParentUID(mRootAccount.getUID());
        topLevel = true;
      }
      if (topLevel || (mRootAccount.getUID().equals(account.getParentUID()))) {
        if (account.getName().startsWith(imbalancePrefix)) {
          mapImbalanceAccount.put(account.getName().substring(imbalancePrefix.length()), account);
        }
      }
    }

    // Set the account for created balancing splits to correct imbalance accounts
    for (Split split : mAutoBalanceSplits) {
      String currencyCode = split.getAccountUID();
      Account imbAccount = mapImbalanceAccount.get(currencyCode);
      if (imbAccount == null) {
        imbAccount =
            new Account(imbalancePrefix + currencyCode, Currency.getInstance(currencyCode));
        imbAccount.setParentUID(mRootAccount.getUID());
        imbAccount.setAccountType(AccountType.BANK);
        mapImbalanceAccount.put(currencyCode, imbAccount);
        mAccountList.add(imbAccount);
      }
      split.setAccountUID(imbAccount.getUID());
    }

    java.util.Stack<Account> stack = new Stack<>();
    for (Account account : mAccountList) {
      if (mapFullName.get(account.getUID()) != null) {
        continue;
      }
      stack.push(account);
      String parentAccountFullName;
      while (!stack.isEmpty()) {
        Account acc = stack.peek();
        if (acc.getAccountType() == AccountType.ROOT) {
          // ROOT_ACCOUNT_FULL_NAME should ensure ROOT always sorts first
          mapFullName.put(acc.getUID(), AccountsDbAdapter.ROOT_ACCOUNT_FULL_NAME);
          stack.pop();
          continue;
        }
        String parentUID = acc.getParentUID();
        Account parentAccount = mAccountMap.get(parentUID);
        // ROOT account will be added if not exist, so now anly ROOT
        // has an empty parent
        if (parentAccount.getAccountType() == AccountType.ROOT) {
          // top level account, full name is the same as its name
          mapFullName.put(acc.getUID(), acc.getName());
          stack.pop();
          continue;
        }
        parentAccountFullName = mapFullName.get(parentUID);
        if (parentAccountFullName == null) {
          // non-top-level account, parent full name still unknown
          stack.push(parentAccount);
          continue;
        }
        mapFullName.put(
            acc.getUID(),
            parentAccountFullName + AccountsDbAdapter.ACCOUNT_NAME_SEPARATOR + acc.getName());
        stack.pop();
      }
    }
    for (Account account : mAccountList) {
      account.setFullName(mapFullName.get(account.getUID()));
    }
    long startTime = System.nanoTime();
    mAccountsDbAdapter.beginTransaction();
    Log.d(getClass().getSimpleName(), "bulk insert starts");
    try {
      Log.d(getClass().getSimpleName(), "before clean up db");
      mAccountsDbAdapter.deleteAllRecords();
      Log.d(
          getClass().getSimpleName(),
          String.format("deb clean up done %d ns", System.nanoTime() - startTime));
      long nAccounts = mAccountsDbAdapter.bulkAddRecords(mAccountList);
      Log.d("Handler:", String.format("%d accounts inserted", nAccounts));
      // We need to add scheduled actions first because there is a foreign key constraint on
      // transactions
      // which are generated from scheduled actions (we do auto-create some transactions during
      // import)
      long nSchedActions = mScheduledActionsDbAdapter.bulkAddRecords(mScheduledActionsList);
      Log.d("Handler:", String.format("%d scheduled actions inserted", nSchedActions));

      long nTempTransactions = mTransactionsDbAdapter.bulkAddRecords(mTemplateTransactions);
      Log.d("Handler:", String.format("%d template transactions inserted", nTempTransactions));

      long nTransactions = mTransactionsDbAdapter.bulkAddRecords(mTransactionList);
      Log.d("Handler:", String.format("%d transactions inserted", nTransactions));

      long nPrices = mPricesDbAdapter.bulkAddRecords(mPriceList);
      Log.d(getClass().getSimpleName(), String.format("%d prices inserted", nPrices));

      long endTime = System.nanoTime();
      Log.d(getClass().getSimpleName(), String.format("bulk insert time: %d", endTime - startTime));

      mAccountsDbAdapter.setTransactionSuccessful();
    } finally {
      mAccountsDbAdapter.endTransaction();
    }
  }