/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import android.text.TextUtils;
import android.util.Log;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
import com.todoroo.astrid.subtasks.OrderedMetadataListUpdater;
import com.todoroo.astrid.subtasks.OrderedMetadataListUpdater.OrderedListIterator;
public class GtasksTaskListUpdater extends OrderedMetadataListUpdater<StoreObject> {
/** map of task -> parent task */
final HashMap<Long, Long> parents = new HashMap<Long, Long>();
/** map of task -> prior sibling */
final HashMap<Long, Long> siblings = new HashMap<Long, Long>();
final HashMap<Long, String> localToRemoteIdMap =
new HashMap<Long, String>();
@Autowired private GtasksListService gtasksListService;
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private GtasksSyncService gtasksSyncService;
@Autowired private MetadataDao metadataDao;
public GtasksTaskListUpdater() {
super();
}
// --- overrides
@Override
protected IntegerProperty indentProperty() {
return GtasksMetadata.INDENT;
}
@Override
protected LongProperty orderProperty() {
return GtasksMetadata.ORDER;
}
@Override
protected LongProperty parentProperty() {
return GtasksMetadata.PARENT_TASK;
}
@Override
protected Metadata getTaskMetadata(StoreObject list, long taskId) {
return gtasksMetadataService.getTaskMetadata(taskId);
}
@Override
protected Metadata createEmptyMetadata(StoreObject list, long taskId) {
Metadata metadata = GtasksMetadata.createEmptyMetadata(taskId);
metadata.setValue(GtasksMetadata.LIST_ID, list.getValue(GtasksList.REMOTE_ID));
return metadata;
}
@Override
protected void beforeIndent(StoreObject list) {
updateParentSiblingMapsFor(list);
}
@Override
protected void iterateThroughList(Filter filter, StoreObject list, OrderedListIterator iterator) {
gtasksMetadataService.iterateThroughList(list, iterator);
}
@Override
protected void onMovedOrIndented(Metadata metadata) {
gtasksSyncService.triggerMoveForMetadata(metadata);
}
// --- used during synchronization
/**
* Create a local tree of tasks to expedite sibling and parent lookups
*/
public void createParentSiblingMaps() {
for(StoreObject list : gtasksListService.getLists()) {
updateParentSiblingMapsFor(list);
}
}
/**
* Update order, parent, and indentation fields for all tasks in the given list
* @param listId
*/
public void correctMetadataForList(String listId) {
StoreObject list = gtasksListService.getList(listId);
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
return;
updateParentSiblingMapsFor(list);
final AtomicLong order = new AtomicLong(0);
final AtomicInteger previousIndent = new AtomicInteger(-1);
gtasksMetadataService.iterateThroughList(list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
metadata.setValue(GtasksMetadata.ORDER, order.getAndAdd(1));
int indent = metadata.getValue(GtasksMetadata.INDENT);
if(indent > previousIndent.get() + 1)
indent = previousIndent.get() + 1;
metadata.setValue(GtasksMetadata.INDENT, indent);
Long parent = parents.get(taskId);
if(parent == null || parent < 0)
parent = Task.NO_ID;
metadata.setValue(GtasksMetadata.PARENT_TASK, parent);
PluginServices.getMetadataService().save(metadata);
previousIndent.set(indent);
}
});
}
public void correctOrderAndIndentForList(String listId) {
orderAndIndentHelper(listId, new AtomicLong(0L), Task.NO_ID, 0,
new HashSet<Long>());
}
private void orderAndIndentHelper(String listId, AtomicLong order, long parent, int indentLevel,
HashSet<Long> alreadyChecked) {
TodorooCursor<Metadata> metadata = metadataDao.query(Query.select(Metadata.PROPERTIES)
.where(Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY),
GtasksMetadata.LIST_ID.eq(listId), GtasksMetadata.PARENT_TASK.eq(parent)))
.orderBy(Order.asc(Functions.cast(GtasksMetadata.GTASKS_ORDER, "INTEGER")))); //$NON-NLS-1$
try {
if (metadata.getCount() > 0) {
Metadata curr = new Metadata();
for (metadata.moveToFirst(); !metadata.isAfterLast(); metadata.moveToNext()) {
curr.readFromCursor(metadata);
if(alreadyChecked.contains(curr.getValue(Metadata.TASK)))
continue;
curr.setValue(GtasksMetadata.INDENT, indentLevel);
curr.setValue(GtasksMetadata.ORDER, order.getAndIncrement());
metadataDao.saveExisting(curr);
alreadyChecked.add(curr.getValue(Metadata.TASK));
orderAndIndentHelper(listId, order, curr.getValue(Metadata.TASK), indentLevel + 1, alreadyChecked);
}
}
} finally {
metadata.close();
}
}
private void updateParentSiblingMapsFor(StoreObject list) {
final AtomicLong previousTask = new AtomicLong(Task.NO_ID);
final AtomicInteger previousIndent = new AtomicInteger(-1);
gtasksMetadataService.iterateThroughList(list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(GtasksMetadata.INDENT);
try {
long parent, sibling;
if(indent > previousIndent.get()) {
parent = previousTask.get();
sibling = Task.NO_ID;
} else if(indent == previousIndent.get()) {
sibling = previousTask.get();
parent = parents.get(sibling);
} else {
// move up once for each indent
sibling = previousTask.get();
for(int i = indent; i < previousIndent.get(); i++)
sibling = parents.get(sibling);
if(parents.containsKey(sibling))
parent = parents.get(sibling);
else
parent = Task.NO_ID;
}
parents.put(taskId, parent);
siblings.put(taskId, sibling);
} catch (Exception e) {
Log.e("gtasks-task-updating", "Caught exception", e); //$NON-NLS-1$ //$NON-NLS-2$
}
previousTask.set(taskId);
previousIndent.set(indent);
if(!TextUtils.isEmpty(metadata.getValue(GtasksMetadata.ID)))
localToRemoteIdMap.put(taskId, metadata.getValue(GtasksMetadata.ID));
}
});
}
}