Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read gzip/zip file. #253

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/src/main/java/org/gnucash/android/app/GnuCashApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,18 @@ public static boolean shouldBackupTransactions(Context context) {
return sharedPrefs.getBoolean(context.getString(R.string.key_delete_transaction_backup), true);
}

/**
* Returns <code>true</code> if setting is enabled to backup the book before importing a book,
* <code>false</code> otherwise.
*
* @param context The context.
* @return <code>true</code> if the book should be backed-up.
*/
public static boolean shouldBackupForImport(Context context) {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
return sharedPrefs.getBoolean(context.getString(R.string.key_import_book_backup), true);
}

/**
* Get the default transaction type.
*
Expand Down
78 changes: 58 additions & 20 deletions app/src/main/java/org/gnucash/android/importer/GncXmlImporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
*/
package org.gnucash.android.importer;

import static java.util.zip.GZIPInputStream.GZIP_MAGIC;

import androidx.annotation.NonNull;

import org.gnucash.android.db.adapter.TransactionsDbAdapter;
import org.gnucash.android.model.Book;
import org.gnucash.android.util.PreferencesHelper;
Expand All @@ -24,10 +28,12 @@
import org.xml.sax.XMLReader;

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
Expand All @@ -42,6 +48,10 @@
*/
public class GncXmlImporter {

private static final int ZIP_MAGIC = 0x504B0304;
private static final int ZIP_MAGIC_EMPTY = 0x504B0506;
private static final int ZIP_MAGIC_SPANNED = 0x504B0708;

/**
* Parse GnuCash XML input and populates the database
*
Expand All @@ -58,37 +68,65 @@ public static String parse(InputStream gncXmlInputStream) throws ParserConfigura
* @param gncXmlInputStream InputStream source of the GnuCash XML file
* @return the book into which the XML was imported
*/
public static Book parseBook(InputStream gncXmlInputStream) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();

BufferedInputStream bos;
PushbackInputStream pb = new PushbackInputStream(gncXmlInputStream, 2); //we need a pushbackstream to look ahead
byte[] signature = new byte[2];
pb.read(signature); //read the signature
pb.unread(signature); //push back the signature to the stream
if (signature[0] == (byte) 0x1f && signature[1] == (byte) 0x8b) //check if matches standard gzip magic number
bos = new BufferedInputStream(new GZIPInputStream(pb));
else
bos = new BufferedInputStream(pb);

public static Book parseBook(@NonNull InputStream gncXmlInputStream) throws ParserConfigurationException, SAXException, IOException {
//TODO: Set an error handler which can log errors
Timber.d("Start import");
InputStream input = getInputStream(gncXmlInputStream);
GncXmlHandler handler = new GncXmlHandler();
xr.setContentHandler(handler);
XMLReader reader = createXMLReader(handler);

long startTime = System.nanoTime();
xr.parse(new InputSource(bos));
reader.parse(new InputSource(input));
long endTime = System.nanoTime();
Timber.d("%d ns spent on importing the file", endTime - startTime);

Book book = handler.getImportedBook();
String bookUID = book.getUID();
PreferencesHelper.setLastExportTime(
TransactionsDbAdapter.getInstance().getTimestampOfLastModification(),
bookUID
TransactionsDbAdapter.getInstance().getTimestampOfLastModification(),
bookUID
);

return book;
}

@NonNull
private static InputStream getInputStream(InputStream inputStream) throws IOException {
BufferedInputStream bis = new BufferedInputStream(inputStream);
bis.mark(4);
int byte0 = bis.read();
if (byte0 == -1) throw new EOFException("file too small");
int byte1 = bis.read();
if (byte1 == -1) throw new EOFException("file too small");
int byte2 = bis.read();
if (byte2 == -1) throw new EOFException("file too small");
int byte3 = bis.read();
if (byte3 == -1) throw new EOFException("file too small");
bis.reset(); //push back the signature to the stream

int signature2 = ((byte1 & 0xFF) << 8) | (byte0 & 0xFF);
//check if matches standard gzip magic number
if (signature2 == GZIP_MAGIC) {
return new GZIPInputStream(bis);
}

int signature4 = ((byte3 & 0xFF) << 24) | ((byte2 & 0xFF) << 16) | signature2;
if ((signature4 == ZIP_MAGIC) || (signature4 == ZIP_MAGIC_EMPTY) || (signature4 == ZIP_MAGIC_SPANNED)) {
ZipInputStream zis = new ZipInputStream(bis);
ZipEntry entry = zis.getNextEntry();
if (entry != null) {
return zis;
}
}

return bis;
}

private static XMLReader createXMLReader(GncXmlHandler handler) throws ParserConfigurationException, SAXException {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
xr.setContentHandler(handler);
return xr;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,15 @@ protected String doInBackground(Uri... uris) {
if (mBackup) {
BackupManager.backupActiveBook();
}
if (isCancelled()) {
return null;
}

Uri uri = uris[0];
ContentResolver contentResolver = mContext.getContentResolver();
Book book;
String bookUID;
try {
ContentResolver contentResolver = mContext.getContentResolver();
InputStream accountInputStream = contentResolver.openInputStream(uri);
book = GncXmlImporter.parseBook(accountInputStream);
book.setSourceUri(uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,8 @@ public static void startXmlFileChooser(Fragment fragment) {
* @param onFinishTask Task to be executed when import is complete
*/
public static void importXmlFileFromIntent(Activity context, Intent data, TaskDelegate onFinishTask) {
new ImportAsyncTask(context, onFinishTask, true).execute(data.getData());
boolean backup = GnuCashApplication.shouldBackupForImport(context);
new ImportAsyncTask(context, onFinishTask, backup).execute(data.getData());
}

/**
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/donottranslate.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<string name="key_backup_location" translatable="false">backup_location</string>
<string name="key_export_accounts_csv" translatable="false">export_accounts_csv_key</string>
<string name="key_delete_transaction_backup" translatable="false">delete_transaction_backup</string>
<string name="key_import_book_backup" translatable="false">import_book_backup</string>
<!-- This is the filename for default backups. So use only simple characters and no spaces. Do not change the extension -->
<string name="label_backup_filename" translatable="false">gnucash_pocket_backup.gnca</string>
<string-array name="key_account_type_entries" translatable="false">
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,10 @@
<string name="title_dropbox_sync_preference">Enable DropBox</string>
<string name="title_owncloud_sync_preference">Enable ownCloud </string>
<string name="title_backup_preference_category">Backup</string>
<string name="title_delete_transaction_backup">When delete transaction?</string>
<string name="summary_delete_transaction_backup">Backup active book when delete a transaction?</string>
<string name="title_delete_transaction_backup">When deleting a transaction?</string>
<string name="summary_delete_transaction_backup">Backup the active book before deleting a transaction?</string>
<string name="title_import_book_backup">When importing a book?</string>
<string name="summary_import_book_backup">Backup the active book before importing a book?</string>
<string name="summary_dropbox_sync">Enable exporting to DropBox</string>
<string name="summary_owncloud_sync">Enable exporting to ownCloud</string>
<string name="title_backup_prefs">Backup Preferences</string>
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/xml/fragment_backup_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@
android:key="@string/key_delete_transaction_backup"
android:summary="@string/summary_delete_transaction_backup"
android:title="@string/title_delete_transaction_backup" />

<androidx.preference.SwitchPreference
android:defaultValue="true"
android:key="@string/key_import_book_backup"
android:summary="@string/summary_import_book_backup"
android:title="@string/title_import_book_backup" />
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory android:title="@string/title_export_preference_category">

Expand Down