SyncState.java
/*
* Copyright (c) 2014-present, salesforce.com, inc.
* All rights reserved.
* Redistribution and use of this software in source and binary forms, with or
* without modification, are permitted provided that the following conditions
* are met:
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of salesforce.com, inc. nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission of salesforce.com, inc.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.salesforce.androidsdk.smartsync.util;
import com.salesforce.androidsdk.smartstore.store.IndexSpec;
import com.salesforce.androidsdk.smartstore.store.SmartStore;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* State of a sync-down or sync-up
*/
public class SyncState {
// SmartStore
public static final String SYNCS_SOUP = "syncs_soup";
public static final String SYNC_TYPE = "type";
public static final String SYNC_TARGET = "target";
public static final String SYNC_OPTIONS = "options";
public static final String SYNC_SOUP_NAME = "soupName";
public static final String SYNC_STATUS = "status";
public static final String SYNC_PROGRESS = "progress";
public static final String SYNC_TOTAL_SIZE = "totalSize";
public static final String SYNC_MAX_TIME_STAMP = "maxTimeStamp";
private long id;
private Type type;
private SyncTarget target;
private SyncOptions options;
private String soupName;
private Status status;
private int progress;
private int totalSize;
private long maxTimeStamp;
/**
* Create syncs soup if needed
* @param store
*/
public static void setupSyncsSoupIfNeeded(SmartStore store) {
if (store.hasSoup(SYNCS_SOUP))
return;
final IndexSpec[] indexSpecs = {
new IndexSpec(SYNC_TYPE, SmartStore.Type.string)
};
store.registerSoup(SYNCS_SOUP, indexSpecs);
}
/**
* Create sync state in database for a sync down and return corresponding SyncState
* @return
* @throws JSONException
*/
public static SyncState createSyncDown(SmartStore store, SyncDownTarget target, SyncOptions options, String soupName) throws JSONException {
JSONObject sync = new JSONObject();
sync.put(SYNC_TYPE, Type.syncDown);
sync.put(SYNC_TARGET, target.asJSON());
sync.put(SYNC_OPTIONS, options.asJSON());
sync.put(SYNC_SOUP_NAME, soupName);
sync.put(SYNC_STATUS, Status.NEW.name());
sync.put(SYNC_PROGRESS, 0);
sync.put(SYNC_TOTAL_SIZE, -1);
sync.put(SYNC_MAX_TIME_STAMP, -1);
sync = store.upsert(SYNCS_SOUP, sync);
return SyncState.fromJSON(sync);
}
/**
* Create sync state in database for a sync up and return corresponding SyncState
* @return
* @throws JSONException
*/
public static SyncState createSyncUp(SmartStore store, SyncUpTarget target, SyncOptions options, String soupName) throws JSONException {
JSONObject sync = new JSONObject();
sync.put(SYNC_TYPE, Type.syncUp);
sync.put(SYNC_TARGET, target.asJSON());
sync.put(SYNC_SOUP_NAME, soupName);
sync.put(SYNC_OPTIONS, options.asJSON());
sync.put(SYNC_STATUS, Status.NEW.name());
sync.put(SYNC_PROGRESS, 0);
sync.put(SYNC_TOTAL_SIZE, -1);
sync.put(SYNC_MAX_TIME_STAMP, -1);
sync = store.upsert(SYNCS_SOUP, sync);
return SyncState.fromJSON(sync);
}
/**
* Build SyncState from json
* @param sync
* @return
* @throws JSONException
*/
public static SyncState fromJSON(JSONObject sync) throws JSONException {
SyncState state = new SyncState();
state.id = sync.getLong(SmartStore.SOUP_ENTRY_ID);
state.type = Type.valueOf(sync.getString(SYNC_TYPE));
final JSONObject jsonTarget = sync.optJSONObject(SYNC_TARGET);
state.target = (state.type == Type.syncDown ? SyncDownTarget.fromJSON(jsonTarget) : SyncUpTarget.fromJSON(jsonTarget));
state.options = SyncOptions.fromJSON(sync.optJSONObject(SYNC_OPTIONS));
state.soupName = sync.getString(SYNC_SOUP_NAME);
state.status = Status.valueOf(sync.getString(SYNC_STATUS));
state.progress = sync.getInt(SYNC_PROGRESS);
state.totalSize = sync.getInt(SYNC_TOTAL_SIZE);
state.maxTimeStamp = sync.optLong(SYNC_MAX_TIME_STAMP, -1);
return state;
}
/**
* Build SyncState from store sync given by id
* @param store
* @param id
* @return
* @throws JSONException
*/
public static SyncState byId(SmartStore store, long id) throws JSONException {
JSONArray syncs = store.retrieve(SYNCS_SOUP, id);
if (syncs == null || syncs.length() == 0)
return null;
return SyncState.fromJSON(syncs.getJSONObject(0));
}
/**
* @return json representation of sync
* @throws JSONException
*/
public JSONObject asJSON() throws JSONException {
JSONObject sync = new JSONObject();
sync.put(SmartStore.SOUP_ENTRY_ID, id);
sync.put(SYNC_TYPE, type.name());
if (target != null) sync.put(SYNC_TARGET, target.asJSON());
if (options != null) sync.put(SYNC_OPTIONS, options.asJSON());
sync.put(SYNC_SOUP_NAME, soupName);
sync.put(SYNC_STATUS, status.name());
sync.put(SYNC_PROGRESS, progress);
sync.put(SYNC_TOTAL_SIZE, totalSize);
sync.put(SYNC_MAX_TIME_STAMP, maxTimeStamp);
return sync;
}
/**
* Save SyncState to db
* @param store
* @throws JSONException
*/
public void save(SmartStore store) throws JSONException {
store.update(SYNCS_SOUP, asJSON(), getId());
}
public long getId() {
return id;
}
public Type getType() {
return type;
}
public SyncTarget getTarget() {
return target;
}
public SyncOptions getOptions() {
return options;
}
public MergeMode getMergeMode() {
return (options != null && options.getMergeMode() != null) ? options.getMergeMode() : MergeMode.OVERWRITE;
}
public String getSoupName() {
return soupName;
}
public Status getStatus() {
return status;
}
public int getProgress() {
return progress;
}
public int getTotalSize() {
return totalSize;
}
public long getMaxTimeStamp() {
return maxTimeStamp;
}
public void setMaxTimeStamp(long maxTimeStamp) {
this.maxTimeStamp = maxTimeStamp;
}
public void setProgress(int progress) {
this.progress = progress;
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
public void setStatus(Status status) {
this.status = status;
}
public boolean isDone() {
return this.status == Status.DONE;
}
public boolean hasFailed() {
return this.status == Status.FAILED;
}
public boolean isRunning() {
return this.status == Status.RUNNING;
}
public SyncState copy() throws JSONException {
return SyncState.fromJSON(asJSON());
}
/**
* Enum for sync type
*/
public enum Type {
syncDown,
syncUp
}
/**
* Enum for sync status
*
*/
public enum Status {
NEW,
RUNNING,
DONE,
FAILED
}
/**
* Enum for merge modes
*/
public enum MergeMode {
OVERWRITE,
LEAVE_IF_CHANGED;
}
}