package org.commcare.android.database.user.models;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import org.commcare.CommCareApplication;
import org.commcare.android.storage.framework.Persisted;
import org.commcare.logging.AndroidLogger;
import org.commcare.models.database.SqlStorage;
import org.commcare.models.framework.Persisting;
import org.commcare.models.framework.Table;
import org.commcare.modern.models.EncryptedModel;
import org.commcare.modern.models.MetaField;
import org.commcare.provider.InstanceProviderAPI.InstanceColumns;
import org.commcare.utils.StorageUtils;
import org.javarosa.core.services.Logger;
import java.io.FileNotFoundException;
import java.util.Date;
import java.util.Vector;
/**
* @author ctsims
*/
@Table(FormRecord.STORAGE_KEY)
public class FormRecord extends Persisted implements EncryptedModel {
public static final String STORAGE_KEY = "FORMRECORDS";
public static final String META_INSTANCE_URI = "INSTANCE_URI";
public static final String META_STATUS = "STATUS";
public static final String META_UUID = "UUID";
public static final String META_XMLNS = "XMLNS";
public static final String META_LAST_MODIFIED = "DATE_MODIFIED";
public static final String META_APP_ID = "APP_ID";
public static final String META_SUBMISSION_ORDERING_NUMBER = "SUBMISSION_ORDERING_NUMBER";
/**
* This form record is a stub that hasn't actually had data saved for it yet
*/
public static final String STATUS_UNSTARTED = "unstarted";
/**
* This form has been saved, but has not yet been marked as completed and ready for processing
*/
public static final String STATUS_INCOMPLETE = "incomplete";
/**
* User entry on this form has finished, but the form has not been processed yet
*/
public static final String STATUS_COMPLETE = "complete";
/**
* The form has been processed and is ready to be sent to the server *
*/
public static final String STATUS_UNSENT = "unsent";
/**
* This form has been fully processed and is being retained for viewing in the future
*/
public static final String STATUS_SAVED = "saved";
/**
* This form was complete, but something blocked it from processing and it's in a damaged
* state (a.k.a. "quarantined")
*/
public static final String STATUS_LIMBO = "limbo";
/**
* This form has been downloaded, but not processed for metadata
*/
public static final String STATUS_UNINDEXED = "unindexed";
@Persisting(1)
@MetaField(META_XMLNS)
private String xmlns;
@Persisting(2)
@MetaField(META_INSTANCE_URI)
private String instanceURI;
@Persisting(3)
@MetaField(META_STATUS)
private String status;
@Persisting(4)
private byte[] aesKey;
@Persisting(value = 5, nullable = true)
@MetaField(META_UUID)
private String uuid;
@Persisting(6)
@MetaField(META_LAST_MODIFIED)
private Date lastModified;
@Persisting(7)
@MetaField(META_APP_ID)
private String appId;
@Persisting(value = 8, nullable = true)
@MetaField(META_SUBMISSION_ORDERING_NUMBER)
private String submissionOrderingNumber;
public FormRecord() {
}
/**
* Creates a record of a form entry with the provided data. Note that none
* of the parameters can be null...
*/
public FormRecord(String instanceURI, String status, String xmlns, byte[] aesKey, String uuid,
Date lastModified, String appId) {
this.instanceURI = instanceURI;
this.status = status;
this.xmlns = xmlns;
this.aesKey = aesKey;
this.uuid = uuid;
this.lastModified = lastModified;
if (lastModified == null) {
this.lastModified = new Date();
}
this.appId = appId;
}
/**
* Create a copy of the current form record, with an updated instance uri
* and status.
*/
public FormRecord updateInstanceAndStatus(String instanceURI, String newStatus) {
FormRecord fr = new FormRecord(instanceURI, newStatus, xmlns, aesKey, uuid,
lastModified, appId);
fr.recordId = this.recordId;
fr.submissionOrderingNumber = this.submissionOrderingNumber;
return fr;
}
public Uri getInstanceURI() {
if ("".equals(instanceURI)) {
return null;
}
return Uri.parse(instanceURI);
}
public byte[] getAesKey() {
return aesKey;
}
public String getStatus() {
return status;
}
public Date lastModified() {
return lastModified;
}
public String getFormNamespace() {
return xmlns;
}
@Override
public boolean isEncrypted(String data) {
return false;
}
@Override
public boolean isBlobEncrypted() {
return true;
}
public String getAppId() {
return this.appId;
}
public String getInstanceID() {
return uuid;
}
public int getSubmissionOrderingNumber() {
if (submissionOrderingNumber == null) {
return -1;
}
return Integer.parseInt(submissionOrderingNumber);
}
/**
* Get the file system path to the encrypted XML submission file.
*
* @param context Android context
* @return A string containing the location of the encrypted XML instance for this form
* @throws FileNotFoundException If there isn't a record available defining a path for this form
*/
public String getPath(Context context) throws FileNotFoundException {
Uri uri = getInstanceURI();
if (uri == null) {
throw new FileNotFoundException("No form instance URI exists for formrecord " + recordId);
}
Cursor c = null;
try {
c = context.getContentResolver().query(uri, new String[]{InstanceColumns.INSTANCE_FILE_PATH}, null, null, null);
if (c == null || !c.moveToFirst()) {
throw new FileNotFoundException("No Instances were found at for formrecord " + recordId + " at isntance URI " + uri.toString());
}
return c.getString(c.getColumnIndex(InstanceColumns.INSTANCE_FILE_PATH));
} finally {
if (c != null) {
c.close();
}
}
}
@Override
public String toString() {
return String.format("Form Record[%s][Status: %s]\n[Form: %s]\n[Last Modified: %s]", this.recordId, this.status, this.xmlns, this.lastModified.toString());
}
public void setFormNumberForSubmissionOrdering(int num) {
this.submissionOrderingNumber = ""+num;
}
public void logPendingDeletion(String classTag, String reason) {
String logMessage = String.format(
"Wiping form record with id %s and submission ordering number %s " +
"in class %s because %s",
getInstanceID(),
getSubmissionOrderingNumber(),
classTag, reason);
Logger.log(AndroidLogger.TYPE_FORM_DELETION, logMessage);
}
}