<commit_before>package org.odk.collect.android.tasks;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

import org.commcare.android.crypt.EncryptionIO;
import org.commcare.android.logging.AndroidLogger;
import org.commcare.android.tasks.templates.CommCareTask;
import org.commcare.dalvik.odk.provider.FormsProviderAPI.FormsColumns;
import org.commcare.dalvik.odk.provider.InstanceProviderAPI;
import org.commcare.dalvik.odk.provider.InstanceProviderAPI.InstanceColumns;
import org.commcare.dalvik.preferences.DeveloperPreferences;
import org.javarosa.core.io.StreamsUtil;
import org.javarosa.core.model.FormDef;
import org.javarosa.core.model.FormIndex;
import org.javarosa.core.model.instance.FormInstance;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.transport.payload.ByteArrayPayload;
import org.javarosa.form.api.FormEntryController;
import org.javarosa.model.xform.XFormSerializingVisitor;
import org.odk.collect.android.activities.FormEntryActivity;
import org.odk.collect.android.listeners.FormSavedListener;
import org.odk.collect.android.logic.FormController;
import org.odk.collect.android.utilities.EncryptionUtils;
import org.odk.collect.android.utilities.EncryptionUtils.EncryptedFormInformation;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.crypto.spec.SecretKeySpec;

/**
 * @author <NAME> (<EMAIL>)
 * @author <NAME> (<EMAIL>)
 */
public class SaveToDiskTask extends CommCareTask<Void, String, SaveToDiskTask.SaveStatus, FormEntryActivity> {
    // callback to run upon saving
    private FormSavedListener mSavedListener;
    private final Boolean exitAfterSave;
    private final Boolean mMarkCompleted;
    // URI to the thing we are saving
    private Uri mUri;
    // The name of the form we are saving
    private final String mInstanceName;
    private final Context context;
    // URI to the table we are saving to
    private final Uri instanceContentUri;

    private final SecretKeySpec symetricKey;
    private String errorMessage = "";

    public enum SaveStatus {
        SAVED_COMPLETE,
        SAVED_INCOMPLETE,
        INVALID_ANSWER,
        SAVED_AND_EXIT
    }

    public static final int SAVING_TASK_ID = 17;

    public SaveToDiskTask(Uri mUri, Boolean saveAndExit, Boolean markCompleted, String updatedName, Context context, Uri instanceContentUri, SecretKeySpec symetricKey, boolean headless) {
        TAG = SaveToDiskTask.class.getSimpleName();

        this.mUri = mUri;
        exitAfterSave = saveAndExit;
        mMarkCompleted = markCompleted;
        mInstanceName = updatedName;
        this.context = context;
        this.instanceContentUri = instanceContentUri;
        this.symetricKey = symetricKey;

        if (headless) {
            this.taskId = -1;
        } else {
            this.taskId = SAVING_TASK_ID;
        }
    }

    /**
     * Initialize {@link FormEntryController} with {@link FormDef} from binary or from XML. If given
     * an instance, it will be used to fill the {@link FormDef}.
     */
    @Override
    protected SaveStatus doTaskBackground(Void... nothing) {
        if (hasInvalidAnswers(mMarkCompleted, DeveloperPreferences.shouldFireTriggersOnSave())) {
            return SaveStatus.INVALID_ANSWER;
        }

        FormEntryActivity.mFormController.postProcessInstance();

        exportData(mMarkCompleted);

        if (exitAfterSave) {
            return SaveStatus.SAVED_AND_EXIT;
        } else if (mMarkCompleted) {
            return SaveStatus.SAVED_COMPLETE;
        } else {
            return SaveStatus.SAVED_INCOMPLETE;
        }
    }

    /**
     * Update or create a new entry in the form table for the
     */
    private void updateInstanceDatabase(boolean incomplete, boolean canEditAfterCompleted) {
        ContentValues values = new ContentValues();
        if (mInstanceName != null) {
            values.put(InstanceColumns.DISPLAY_NAME, mInstanceName);
        }

        if (incomplete || !mMarkCompleted) {
            values.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_INCOMPLETE);
        } else {
            values.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_COMPLETE);
        }
        // update this whether or not the status is complete.
        values.put(InstanceColumns.CAN_EDIT_WHEN_COMPLETE, Boolean.toString(canEditAfterCompleted));

        // Insert or update the form instance into the database.
        String resolverType = context.getContentResolver().getType(mUri);
        if (InstanceColumns.CONTENT_ITEM_TYPE.equals(resolverType)) {
            // Started with a concrete instance (e.i. by editing an existing
            // form), so just update it.
            context.getContentResolver().update(mUri, values, null, null);
        } else if (FormsColumns.CONTENT_ITEM_TYPE.equals(resolverType)) {
            // Started with an empty form or possibly a manually saved form.
            // Try updating, and create a new instance if that fails.
            String[] whereArgs = {FormEntryActivity.mInstancePath};
            int rowsUpdated = context.getContentResolver().update(instanceContentUri, values,
                    InstanceColumns.INSTANCE_FILE_PATH + "=?", whereArgs);
            if (rowsUpdated == 0) {
                // Form instance didn't exist in the table, so create it.
                Log.e(TAG, "No instance found, creating");
                Cursor c = null;
                try {
                    // grab the first entry in the instance table for the form
                    c = context.getContentResolver().query(mUri, null, null, null, null);
                    c.moveToFirst();
                    // copy data out of that entry, into the entry we are creating
                    values.put(InstanceColumns.JR_FORM_ID,
                            c.getString(c.getColumnIndex(FormsColumns.JR_FORM_ID)));
                    values.put(InstanceColumns.SUBMISSION_URI,
                            c.getString(c.getColumnIndex(FormsColumns.SUBMISSION_URI)));

                    if (mInstanceName == null) {
                        values.put(InstanceColumns.DISPLAY_NAME,
                                c.getString(c.getColumnIndex(FormsColumns.DISPLAY_NAME)));
                    }
                } finally {
                    if (c != null) {
                        c.close();
                    }
                }
                values.put(InstanceColumns.INSTANCE_FILE_PATH, FormEntryActivity.mInstancePath);
                try {
                    mUri = context.getContentResolver().insert(instanceContentUri, values);
                } catch (IllegalStateException e) {
                    throw new RuntimeException(e);
                }
            } else if (rowsUpdated == 1) {
                Log.i(TAG, "Instance already exists, updating");
            } else{
                Log.w(TAG, "Updated more than one entry, that's not good");
            }
        }
    }

    /**
     * Write's the data to the sdcard, and updates the instances content provider.
     * In theory we don't have to write to disk, and this is where you'd add
     * other methods.
     * @return was writing of data successful?
     */<commit_msg>Update @throws docs on exportData<commit_after>package org.odk.collect.android.tasks;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

import org.commcare.android.crypt.EncryptionIO;
import org.commcare.android.logging.AndroidLogger;
import org.commcare.android.tasks.templates.CommCareTask;
import org.commcare.dalvik.odk.provider.FormsProviderAPI.FormsColumns;
import org.commcare.dalvik.odk.provider.InstanceProviderAPI;
import org.commcare.dalvik.odk.provider.InstanceProviderAPI.InstanceColumns;
import org.commcare.dalvik.preferences.DeveloperPreferences;
import org.javarosa.core.io.StreamsUtil;
import org.javarosa.core.model.FormDef;
import org.javarosa.core.model.FormIndex;
import org.javarosa.core.model.instance.FormInstance;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.transport.payload.ByteArrayPayload;
import org.javarosa.form.api.FormEntryController;
import org.javarosa.model.xform.XFormSerializingVisitor;
import org.odk.collect.android.activities.FormEntryActivity;
import org.odk.collect.android.listeners.FormSavedListener;
import org.odk.collect.android.logic.FormController;
import org.odk.collect.android.utilities.EncryptionUtils;
import org.odk.collect.android.utilities.EncryptionUtils.EncryptedFormInformation;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.crypto.spec.SecretKeySpec;

/**
 * @author <NAME> (<EMAIL>)
 * @author <NAME> (<EMAIL>)
 */
public class SaveToDiskTask extends CommCareTask<Void, String, SaveToDiskTask.SaveStatus, FormEntryActivity> {
    // callback to run upon saving
    private FormSavedListener mSavedListener;
    private final Boolean exitAfterSave;
    private final Boolean mMarkCompleted;
    // URI to the thing we are saving
    private Uri mUri;
    // The name of the form we are saving
    private final String mInstanceName;
    private final Context context;
    // URI to the table we are saving to
    private final Uri instanceContentUri;

    private final SecretKeySpec symetricKey;
    private String errorMessage = "";

    public enum SaveStatus {
        SAVED_COMPLETE,
        SAVED_INCOMPLETE,
        INVALID_ANSWER,
        SAVED_AND_EXIT
    }

    public static final int SAVING_TASK_ID = 17;

    public SaveToDiskTask(Uri mUri, Boolean saveAndExit, Boolean markCompleted, String updatedName, Context context, Uri instanceContentUri, SecretKeySpec symetricKey, boolean headless) {
        TAG = SaveToDiskTask.class.getSimpleName();

        this.mUri = mUri;
        exitAfterSave = saveAndExit;
        mMarkCompleted = markCompleted;
        mInstanceName = updatedName;
        this.context = context;
        this.instanceContentUri = instanceContentUri;
        this.symetricKey = symetricKey;

        if (headless) {
            this.taskId = -1;
        } else {
            this.taskId = SAVING_TASK_ID;
        }
    }

    /**
     * Initialize {@link FormEntryController} with {@link FormDef} from binary or from XML. If given
     * an instance, it will be used to fill the {@link FormDef}.
     */
    @Override
    protected SaveStatus doTaskBackground(Void... nothing) {
        if (hasInvalidAnswers(mMarkCompleted, DeveloperPreferences.shouldFireTriggersOnSave())) {
            return SaveStatus.INVALID_ANSWER;
        }

        FormEntryActivity.mFormController.postProcessInstance();

        exportData(mMarkCompleted);

        if (exitAfterSave) {
            return SaveStatus.SAVED_AND_EXIT;
        } else if (mMarkCompleted) {
            return SaveStatus.SAVED_COMPLETE;
        } else {
            return SaveStatus.SAVED_INCOMPLETE;
        }
    }

    /**
     * Update or create a new entry in the form table for the
     */
    private void updateInstanceDatabase(boolean incomplete, boolean canEditAfterCompleted) {
        ContentValues values = new ContentValues();
        if (mInstanceName != null) {
            values.put(InstanceColumns.DISPLAY_NAME, mInstanceName);
        }

        if (incomplete || !mMarkCompleted) {
            values.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_INCOMPLETE);
        } else {
            values.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_COMPLETE);
        }
        // update this whether or not the status is complete.
        values.put(InstanceColumns.CAN_EDIT_WHEN_COMPLETE, Boolean.toString(canEditAfterCompleted));

        // Insert or update the form instance into the database.
        String resolverType = context.getContentResolver().getType(mUri);
        if (InstanceColumns.CONTENT_ITEM_TYPE.equals(resolverType)) {
            // Started with a concrete instance (e.i. by editing an existing
            // form), so just update it.
            context.getContentResolver().update(mUri, values, null, null);
        } else if (FormsColumns.CONTENT_ITEM_TYPE.equals(resolverType)) {
            // Started with an empty form or possibly a manually saved form.
            // Try updating, and create a new instance if that fails.
            String[] whereArgs = {FormEntryActivity.mInstancePath};
            int rowsUpdated = context.getContentResolver().update(instanceContentUri, values,
                    InstanceColumns.INSTANCE_FILE_PATH + "=?", whereArgs);
            if (rowsUpdated == 0) {
                // Form instance didn't exist in the table, so create it.
                Log.e(TAG, "No instance found, creating");
                Cursor c = null;
                try {
                    // grab the first entry in the instance table for the form
                    c = context.getContentResolver().query(mUri, null, null, null, null);
                    c.moveToFirst();
                    // copy data out of that entry, into the entry we are creating
                    values.put(InstanceColumns.JR_FORM_ID,
                            c.getString(c.getColumnIndex(FormsColumns.JR_FORM_ID)));
                    values.put(InstanceColumns.SUBMISSION_URI,
                            c.getString(c.getColumnIndex(FormsColumns.SUBMISSION_URI)));

                    if (mInstanceName == null) {
                        values.put(InstanceColumns.DISPLAY_NAME,
                                c.getString(c.getColumnIndex(FormsColumns.DISPLAY_NAME)));
                    }
                } finally {
                    if (c != null) {
                        c.close();
                    }
                }
                values.put(InstanceColumns.INSTANCE_FILE_PATH, FormEntryActivity.mInstancePath);
                try {
                    mUri = context.getContentResolver().insert(instanceContentUri, values);
                } catch (IllegalStateException e) {
                    throw new RuntimeException(e);
                }
            } else if (rowsUpdated == 1) {
                Log.i(TAG, "Instance already exists, updating");
            } else{
                Log.w(TAG, "Updated more than one entry, that's not good");
            }
        }
    }

    /**
     * Write's the data to the sdcard, and updates the instances content
     * provider. In theory we don't have to write to disk, and this is where
     * you'd add other methods.
     *
     * @throws RuntimeException      File IO issues
     * @throws IllegalStateException Form record processing issue
     */
{'fraction_non_alphanumeric': 0.08504497247952746, 'fraction_numerical': 0.0006712310377231843, 'mean_word_length': 2.491680337473635, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '134197', 'n_tokens_mistral': 4023, 'n_tokens_neox': 3921, 'n_words': 977}