/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map.Entry;
import android.content.ContentValues;
import com.todoroo.andlib.data.Property.CountProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.data.Metadata;
/**
* Service layer for {@link Metadata}-centered activities.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MetadataService {
public static interface SynchronizeMetadataCallback {
public void beforeDeleteMetadata(Metadata m);
}
@Autowired
private MetadataDao metadataDao;
public MetadataService() {
DependencyInjectionService.getInstance().inject(this);
}
// --- service layer
/**
* Clean up metadata. Typically called on startup
*/
public void cleanup() {
TodorooCursor<Metadata> cursor = metadataDao.fetchDangling(Metadata.ID);
try {
if(cursor.getCount() == 0)
return;
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long id = cursor.getLong(0);
metadataDao.delete(id);
}
} finally {
cursor.close();
}
}
/**
* Query underlying database
* @param query
* @return
*/
public TodorooCursor<Metadata> query(Query query) {
return metadataDao.query(query);
}
/**
* Delete from metadata table where rows match a certain condition
* @param where
*/
public int deleteWhere(Criterion where) {
return metadataDao.deleteWhere(where);
}
/**
* Delete from metadata table where rows match a certain condition
* @param where predicate for which rows to update
* @param metadata values to set
*/
public int update(Criterion where, Metadata metadata) {
return metadataDao.update(where, metadata);
}
/**
* Save a single piece of metadata
* @param metadata
*/
public boolean save(Metadata metadata) {
if(!metadata.containsNonNullValue(Metadata.TASK))
throw new IllegalArgumentException("metadata needs to be attached to a task: " + metadata.getMergedValues()); //$NON-NLS-1$
return metadataDao.persist(metadata);
}
/**
* Synchronize metadata for given task id
* @param id
* @param metadata
* @param metadataKeys
* @return true if there were changes
*/
public boolean synchronizeMetadata(long taskId, ArrayList<Metadata> metadata,
Criterion metadataCriterion, SynchronizeMetadataCallback callback, boolean hardDelete) {
boolean dirty = false;
HashSet<ContentValues> newMetadataValues = new HashSet<ContentValues>();
for(Metadata metadatum : metadata) {
metadatum.setValue(Metadata.TASK, taskId);
metadatum.clearValue(Metadata.CREATION_DATE);
metadatum.clearValue(Metadata.ID);
ContentValues values = metadatum.getMergedValues();
for(Entry<String, Object> entry : values.valueSet()) {
if(entry.getKey().startsWith("value")) //$NON-NLS-1$
values.put(entry.getKey(), entry.getValue().toString());
}
newMetadataValues.add(values);
}
Metadata item = new Metadata();
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).where(Criterion.and(MetadataCriteria.byTask(taskId),
metadataCriterion)));
try {
// try to find matches within our metadata list
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
item.readFromCursor(cursor);
long id = item.getId();
// clear item id when matching with incoming values
item.clearValue(Metadata.ID);
item.clearValue(Metadata.CREATION_DATE);
ContentValues itemMergedValues = item.getMergedValues();
if(newMetadataValues.contains(itemMergedValues)) {
newMetadataValues.remove(itemMergedValues);
continue;
}
// not matched. cut it
item.setId(id);
if (callback != null) {
callback.beforeDeleteMetadata(item);
}
if (hardDelete)
metadataDao.delete(id);
else {
item.setValue(Metadata.DELETION_DATE, DateUtilities.now());
metadataDao.persist(item);
}
dirty = true;
}
} finally {
cursor.close();
}
// everything that remains shall be written
for(ContentValues values : newMetadataValues) {
item.clear();
item.setValue(Metadata.CREATION_DATE, DateUtilities.now());
item.mergeWith(values);
metadataDao.persist(item);
dirty = true;
}
return dirty;
}
public boolean synchronizeMetadata(long taskId, ArrayList<Metadata> metadata,
Criterion metadataCriterion, boolean hardDelete) {
return synchronizeMetadata(taskId, metadata, metadataCriterion, null, hardDelete);
}
/**
* Does metadata with this key and task exist?
*/
public boolean hasMetadata(long id, String key) {
CountProperty count = new CountProperty();
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(
count).where(MetadataCriteria.byTaskAndwithKey(id, key)));
try {
cursor.moveToFirst();
return cursor.get(count) > 0;
} finally {
cursor.close();
}
}
/**
* Deletes the given metadata
* @param metadata
*/
public void delete(Metadata metadata) {
metadataDao.delete(metadata.getId());
}
}