ru.otdelit.astrid.opencrx.sync.OpencrxDataService.java Source code

Java tutorial

Introduction

Here is the source code for ru.otdelit.astrid.opencrx.sync.OpencrxDataService.java

Source

/**
 * See the file "LICENSE" for the full license governing this code.
 */
package ru.otdelit.astrid.opencrx.sync;

import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import ru.otdelit.astrid.opencrx.OpencrxUtilities;
import ru.otdelit.astrid.opencrx.api.ApiUtilities;

import android.content.Context;
import android.text.TextUtils;
import android.text.format.Time;

import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.MetadataApiDao.MetadataCriteria;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.StoreObjectApiDao;
import com.todoroo.astrid.data.StoreObjectApiDao.StoreObjectCriteria;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.data.UpdateApiDao;
import com.todoroo.astrid.sync.SyncMetadataService;
import com.todoroo.astrid.sync.SyncProviderUtilities;

/**
 * Singleton. Provides access to Astrid DataService.
    
 * Adapted from Producteev plugin by Tim Su <tim@todoroo.com>
 *
 * @author Andrey Marchenko <igendou@gmail.com>
 */
public final class OpencrxDataService extends SyncMetadataService<OpencrxTaskContainer> {

    // --- constants
    /** Property for reading tag values */
    public static final StringProperty TAG = Metadata.VALUE1;

    /** Utility for joining tasks with metadata */
    public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK));

    /**
     * ATTENTION: duplicates UpdateAdapter.UPDATE_TASK_COMMENT
     */
    public static final String UPDATE_TASK_COMMENT = "task_comment"; //$NON-NLS-1$

    // --- singleton

    private static OpencrxDataService instance = null;

    public static synchronized OpencrxDataService getInstance() {
        if (instance == null)
            instance = new OpencrxDataService(ContextManager.getContext());
        return instance;
    }

    // --- instance variables

    protected final Context context;

    private StoreObjectApiDao storeObjectDao;
    private UpdateApiDao updateDao;

    private OpencrxDataService(Context context) {
        super(context);
        this.context = context;

        storeObjectDao = new StoreObjectApiDao(context);
        updateDao = new UpdateApiDao(context);
    }

    // --- task and metadata methods

    /**
     * Clears metadata information. Used when user logs out of service
     */
    @Override
    public void clearMetadata() {
        super.clearMetadata();
        storeObjectDao.deleteWhere(StoreObject.TYPE.eq(OpencrxActivityCreator.TYPE));
        storeObjectDao.deleteWhere(StoreObject.TYPE.eq(OpencrxContact.TYPE));
    }

    public TodorooCursor<Task> getSyncedTasks(Property<?>[] properties) {
        TodorooCursor<Task> tasks = taskDao.query(Query.select(Task.ID).orderBy(Order.asc(Task.ID)));

        return joinWithOpencrxMetadata(tasks, true, properties);
    }

    private void readCreators() {
        if (creators == null) {
            creators = readStoreObjects(OpencrxActivityCreator.TYPE);
            creatorExists = new boolean[creators.length];
        }
    }

    private void readContacts() {
        if (contacts == null) {
            contacts = readStoreObjects(OpencrxContact.TYPE);
            contactExists = new boolean[contacts.length];
        }
    }

    /**
     * Reads store objects.
     */
    public StoreObject[] readStoreObjects(String type) {
        StoreObject[] ret;
        TodorooCursor<StoreObject> cursor = storeObjectDao
                .query(Query.select(StoreObject.PROPERTIES).where(StoreObjectCriteria.byType(type)));
        try {
            ret = new StoreObject[cursor.getCount()];
            for (int i = 0; i < ret.length; i++) {
                cursor.moveToNext();
                StoreObject object = new StoreObject(cursor);
                ret[i] = object;
            }
        } finally {
            cursor.close();
        }

        return ret;
    }

    // --- Update methods

    public Update[] readNewComments(Time lastSync, long taskId) {
        Update[] ret = null;
        TodorooCursor<Update> cursor = updateDao.query(
                Query.select(Update.PROPERTIES).where(Criterion.and(Update.ACTION_CODE.eq(UPDATE_TASK_COMMENT),
                        Update.TASK_LOCAL.eq(taskId), Update.CREATION_DATE.gt(lastSync.toMillis(false)))));

        try {
            ret = new Update[cursor.getCount()];
            for (int i = 0; i < ret.length; ++i) {
                cursor.moveToNext();
                Update upd = new Update(cursor);
                ret[i] = upd;
            }
        } finally {
            cursor.close();
        }

        return ret;
    }

    public boolean storeNewComment(String text, long taskId, String taskTitle) {
        Update upd = new Update();

        upd.setValue(Update.ACTION_CODE, UPDATE_TASK_COMMENT);
        upd.setValue(Update.TASK_LOCAL, taskId);
        upd.setValue(Update.MESSAGE, text);
        upd.setValue(Update.USER_ID, 0L);
        upd.setValue(Update.CREATION_DATE, DateUtilities.now());
        upd.setValue(Update.TARGET_NAME, taskTitle);

        return updateDao.save(upd);
    }

    // --- dashboard methods

    private StoreObject[] creators = null;
    private boolean[] creatorExists = null; // array of flags to determine whether creator still exists on remote server

    /**
     * @return a list of creators
     */
    public StoreObject[] getCreators() {
        readCreators();
        return creators;
    }

    /**
     * Reads creators
     * @throws JSONException
     */
    @SuppressWarnings("nls")
    public void updateCreators(JSONArray changedCreators) throws JSONException {
        readCreators();

        for (int i = 0; i < changedCreators.length(); i++) {
            JSONObject remote = changedCreators.getJSONObject(i).getJSONObject("dashboard");
            updateCreator(remote, false);
        }

        // check if there are dashboards which does not exist on remote server
        for (int i = 0; i < creators.length; ++i) {
            if (!creatorExists[i])
                storeObjectDao.delete(creators[i].getId());
        }

        // clear dashboard cache
        creators = null;
        creatorExists = null;
    }

    @SuppressWarnings("nls")
    public StoreObject updateCreator(JSONObject remote, boolean reinitCache) throws JSONException {
        if (reinitCache)
            readCreators();
        long id = remote.getLong("id_dashboard");
        StoreObject local = null;
        for (int i = 0; i < creators.length; ++i) {
            if (creators[i].getValue(OpencrxActivityCreator.REMOTE_ID).equals(id)) {
                local = creators[i];
                creatorExists[i] = true;
                break;
            }
        }

        if (local == null)
            local = new StoreObject();

        local.setValue(StoreObject.TYPE, OpencrxActivityCreator.TYPE);
        local.setValue(OpencrxActivityCreator.REMOTE_ID, id);
        local.setValue(OpencrxActivityCreator.NAME, ApiUtilities.decode(remote.getString("title")));
        local.setValue(OpencrxActivityCreator.CRX_ID, remote.getString("crx_id"));

        storeObjectDao.save(local);
        if (reinitCache)
            creators = null;
        return local;
    }

    // user methods

    private StoreObject[] contacts = null;
    private boolean[] contactExists = null;

    /**
     * @return a list of users
     */
    public StoreObject[] getContacts() {
        readContacts();
        return contacts;
    }

    /**
     * Reads users
     */
    public void updateContacts(OpencrxContact[] remoteUsers) {
        readContacts();

        for (int i = 0; i < remoteUsers.length; i++) {
            OpencrxContact remote = remoteUsers[i];
            updateContact(remote, false);
        }

        // check if there are users which does not exist on remote server
        for (int i = 0; i < contacts.length; ++i) {
            if (!contactExists[i])
                storeObjectDao.delete(contacts[i].getId());
        }

        // clear user cache
        contacts = null;
        contactExists = null;
    }

    public StoreObject updateContact(OpencrxContact remote, boolean reinitCache) {
        if (reinitCache)
            readContacts();

        long id = remote.getId();

        StoreObject local = null;

        for (int i = 0; i < contacts.length; ++i) {
            if (contacts[i].getValue(OpencrxContact.REMOTE_ID).equals(id)) {
                local = contacts[i];
                contactExists[i] = true;
                break;
            }
        }

        if (local == null)
            local = new StoreObject();

        local.setValue(StoreObject.TYPE, OpencrxContact.TYPE);
        local.setValue(OpencrxContact.REMOTE_ID, id);
        local.setValue(OpencrxContact.FIRST_NAME, remote.getFirstname());
        local.setValue(OpencrxContact.LAST_NAME, remote.getLastname());
        local.setValue(OpencrxContact.CRX_ID, remote.getCrxId());

        storeObjectDao.save(local);

        if (reinitCache) {
            contacts = null;
            contactExists = null;
        }

        return local;
    }

    public String getCreatorCrxId(long idCreator) {
        TodorooCursor<StoreObject> res = storeObjectDao.query(Query.select(OpencrxActivityCreator.CRX_ID)
                .where(Criterion.and(StoreObject.TYPE.eq(OpencrxActivityCreator.TYPE),
                        OpencrxActivityCreator.REMOTE_ID.eq(idCreator))));

        try {
            if (res.getCount() > 0) {
                res.moveToFirst();
                String id = res.get(OpencrxActivityCreator.CRX_ID);
                return id;
            } else {
                return null;
            }
        } finally {
            res.close();
        }

    }

    public String getContactCrxId(long idUser) {
        TodorooCursor<StoreObject> res = storeObjectDao.query(Query.select(OpencrxContact.CRX_ID).where(
                Criterion.and(StoreObject.TYPE.eq(OpencrxContact.TYPE), OpencrxContact.REMOTE_ID.eq(idUser))));

        try {
            if (res.getCount() > 0) {
                res.moveToFirst();
                String id = res.get(OpencrxContact.CRX_ID);
                return id;
            } else {
                return null;
            }
        } finally {
            res.close();
        }

    }

    @SuppressWarnings("nls")
    public String getUserName(long idUser) {
        TodorooCursor<StoreObject> res = storeObjectDao
                .query(Query.select(OpencrxContact.LAST_NAME, OpencrxContact.FIRST_NAME).where(Criterion
                        .and(StoreObject.TYPE.eq(OpencrxContact.TYPE), OpencrxContact.REMOTE_ID.eq(idUser))));

        try {
            if (res.getCount() > 0) {
                res.moveToFirst();
                String firstName = res.get(OpencrxContact.FIRST_NAME);
                String lastName = res.get(OpencrxContact.LAST_NAME);

                boolean hasFirstName = !TextUtils.isEmpty(firstName);
                boolean hasLastName = !TextUtils.isEmpty(lastName);

                return TextUtils.concat(hasFirstName ? firstName : "", hasFirstName && hasLastName ? " " : "",
                        hasLastName ? lastName : "").toString();
            } else {
                return null;
            }
        } finally {
            res.close();
        }

    }

    @SuppressWarnings("nls")
    public String getCreatorName(long idCreator) {
        TodorooCursor<StoreObject> res = storeObjectDao.query(Query.select(OpencrxActivityCreator.NAME)
                .where(Criterion.and(StoreObject.TYPE.eq(OpencrxActivityCreator.TYPE),
                        OpencrxActivityCreator.REMOTE_ID.eq(idCreator))));

        try {
            if (res.getCount() > 0) {
                res.moveToFirst();
                String name = res.get(OpencrxActivityCreator.NAME);

                return name;
            } else {
                return null;
            }
        } finally {
            res.close();
        }

    }

    public void deleteTaskAndMetadata(long taskId) {
        taskDao.delete(taskId);
        metadataDao.deleteWhere(Metadata.TASK.eq(taskId));
    }

    @Override
    public OpencrxTaskContainer createContainerFromLocalTask(Task task, ArrayList<Metadata> metadata) {
        return new OpencrxTaskContainer(task, metadata);
    }

    @Override
    public Criterion getLocalMatchCriteria(OpencrxTaskContainer remoteTask) {
        return OpencrxActivity.ID.eq(remoteTask.pdvTask.getValue(OpencrxActivity.ID));
    }

    @Override
    public Criterion getMetadataCriteria() {
        return Criterion.or(MetadataCriteria.withKey(OpencrxActivity.METADATA_KEY),
                MetadataCriteria.withKey(TAG_KEY));
    }

    @Override
    public String getMetadataKey() {
        return OpencrxActivity.METADATA_KEY;
    }

    @Override
    public Criterion getMetadataWithRemoteId() {
        return OpencrxActivity.ID.gt(0);
    }

    @Override
    public SyncProviderUtilities getUtilities() {
        return OpencrxUtilities.INSTANCE;
    }

    private TodorooCursor<Task> joinWithOpencrxMetadata(TodorooCursor<Task> tasks, boolean both,
            Property<?>... properties) {
        try {
            TodorooCursor<Metadata> metadata = getRemoteTaskOpencrxMetadata();
            try {
                ArrayList<Long> matchingRows = new ArrayList<Long>();
                joinRowsOpencrx(tasks, metadata, matchingRows, both);

                return taskDao.query(Query.select(properties)
                        .where(Task.ID.in(matchingRows.toArray(new Long[matchingRows.size()]))));
            } finally {
                metadata.close();
            }
        } finally {
            tasks.close();
        }
    }

    /**
     * Gets cursor across all task metadata for joining
     *
     * @return cursor
     */
    private TodorooCursor<Metadata> getRemoteTaskOpencrxMetadata() {
        return metadataDao.query(Query.select(Metadata.TASK)
                .where(Criterion.and(MetadataCriteria.withKey(getMetadataKey()), OpencrxActivity.ID.gt(1)))
                .orderBy(Order.asc(Metadata.TASK)));
    }

    /**
     * Join rows from two cursors on the first column, assuming its an id column
     * @param left
     * @param right
     * @param matchingRows
     * @param both - if false, returns left join, if true, returns both join
     */
    private static void joinRowsOpencrx(TodorooCursor<?> left, TodorooCursor<?> right, ArrayList<Long> matchingRows,
            boolean both) {

        left.moveToPosition(-1);
        right.moveToFirst();

        while (true) {
            left.moveToNext();
            if (left.isAfterLast())
                break;
            long leftValue = left.getLong(0);

            // advance right until it is equal or bigger
            while (!right.isAfterLast() && right.getLong(0) < leftValue) {
                right.moveToNext();
            }

            if (right.isAfterLast()) {
                if (!both)
                    matchingRows.add(leftValue);
                continue;
            }

            if ((right.getLong(0) == leftValue) == both)
                matchingRows.add(leftValue);
        }
    }

}