mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
7.9 KiB
Java
247 lines
7.9 KiB
Java
package com.todoroo.astrid.tags;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.LinkedHashSet;
|
|
|
|
import com.todoroo.andlib.data.Property.CountProperty;
|
|
import com.todoroo.andlib.data.Property.LongProperty;
|
|
import com.todoroo.andlib.data.Property.StringProperty;
|
|
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.Field;
|
|
import com.todoroo.andlib.sql.Join;
|
|
import com.todoroo.andlib.sql.Order;
|
|
import com.todoroo.andlib.sql.Query;
|
|
import com.todoroo.andlib.sql.QueryTemplate;
|
|
import com.todoroo.astrid.core.PluginServices;
|
|
import com.todoroo.astrid.dao.MetadataDao;
|
|
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
|
|
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
|
|
import com.todoroo.astrid.data.Metadata;
|
|
import com.todoroo.astrid.data.Task;
|
|
import com.todoroo.astrid.service.MetadataService;
|
|
import com.todoroo.astrid.service.TaskService;
|
|
import com.todoroo.astrid.utility.Flags;
|
|
|
|
/**
|
|
* Provides operations for working with tags
|
|
*
|
|
* @author Tim Su <tim@todoroo.com>
|
|
*
|
|
*/
|
|
@SuppressWarnings("nls")
|
|
public final class TagService {
|
|
|
|
// --- public constants
|
|
|
|
/** Metadata key for tag data */
|
|
public static final String KEY = "tags-tag";
|
|
|
|
/** Property for reading tag values */
|
|
public static final StringProperty TAG = Metadata.VALUE1;
|
|
|
|
/** Property for astrid.com remote id */
|
|
public static final LongProperty REMOTE_ID = new LongProperty(Metadata.TABLE, Metadata.VALUE2.name);
|
|
|
|
// --- singleton
|
|
|
|
private static TagService instance = null;
|
|
|
|
public static synchronized TagService getInstance() {
|
|
if(instance == null)
|
|
instance = new TagService();
|
|
return instance;
|
|
}
|
|
|
|
// --- implementation details
|
|
|
|
@Autowired
|
|
private MetadataDao metadataDao;
|
|
|
|
@Autowired
|
|
private TaskService taskService;
|
|
|
|
public TagService() {
|
|
DependencyInjectionService.getInstance().inject(this);
|
|
}
|
|
|
|
/**
|
|
* Property for retrieving count of aggregated rows
|
|
*/
|
|
private static final CountProperty COUNT = new CountProperty();
|
|
public static final Order GROUPED_TAGS_BY_ALPHA = Order.asc(TAG);
|
|
public static final Order GROUPED_TAGS_BY_SIZE = Order.desc(COUNT);
|
|
|
|
/**
|
|
* Helper class for returning a tag/task count pair
|
|
*
|
|
* @author Tim Su <tim@todoroo.com>
|
|
*
|
|
*/
|
|
public static final class Tag {
|
|
public String tag;
|
|
int count;
|
|
long remoteId;
|
|
|
|
public Tag(String tag, int count, long remoteId) {
|
|
this.tag = tag;
|
|
this.count = count;
|
|
this.remoteId = remoteId;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return tag;
|
|
}
|
|
|
|
/**
|
|
* Return SQL selector query for getting tasks with a given tag
|
|
*
|
|
* @param tag
|
|
* @return
|
|
*/
|
|
public QueryTemplate queryTemplate(Criterion criterion) {
|
|
return new QueryTemplate().join(Join.inner(Metadata.TABLE,
|
|
Task.ID.eq(Metadata.TASK))).where(tagEq(tag, criterion));
|
|
}
|
|
|
|
}
|
|
|
|
private static Criterion tagEq(String tag, Criterion additionalCriterion) {
|
|
return Criterion.and(
|
|
MetadataCriteria.withKey(KEY), TAG.eq(tag),
|
|
additionalCriterion);
|
|
}
|
|
public QueryTemplate untaggedTemplate() {
|
|
return new QueryTemplate().where(Criterion.and(
|
|
Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).where(MetadataCriteria.withKey(KEY)))),
|
|
TaskCriteria.isActive(),
|
|
TaskCriteria.ownedByMe(),
|
|
TaskCriteria.isVisible()));
|
|
}
|
|
|
|
/**
|
|
* Return all tags ordered by given clause
|
|
*
|
|
* @param order ordering
|
|
* @param activeStatus criterion for specifying completed or uncompleted
|
|
* @return empty array if no tags, otherwise array
|
|
*/
|
|
public Tag[] getGroupedTags(Order order, Criterion activeStatus) {
|
|
Query query = Query.select(TAG, REMOTE_ID, COUNT).
|
|
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))).
|
|
where(Criterion.and(activeStatus, MetadataCriteria.withKey(KEY))).
|
|
orderBy(order).groupBy(TAG);
|
|
TodorooCursor<Metadata> cursor = metadataDao.query(query);
|
|
try {
|
|
Tag[] array = new Tag[cursor.getCount()];
|
|
for (int i = 0; i < array.length; i++) {
|
|
cursor.moveToNext();
|
|
array[i] = new Tag(cursor.get(TAG), cursor.get(COUNT), cursor.get(REMOTE_ID));
|
|
}
|
|
return array;
|
|
} finally {
|
|
cursor.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return tags on the given task
|
|
*
|
|
* @param taskId
|
|
* @return cursor. PLEASE CLOSE THE CURSOR!
|
|
*/
|
|
public TodorooCursor<Metadata> getTags(long taskId) {
|
|
Query query = Query.select(TAG, REMOTE_ID).where(Criterion.and(MetadataCriteria.withKey(KEY),
|
|
MetadataCriteria.byTask(taskId)));
|
|
return metadataDao.query(query);
|
|
}
|
|
|
|
/**
|
|
* Return tags as a comma-separated list of strings
|
|
*
|
|
* @param taskId
|
|
* @return empty string if no tags, otherwise string
|
|
*/
|
|
public String getTagsAsString(long taskId) {
|
|
return getTagsAsString(taskId, ", ");
|
|
}
|
|
|
|
/**
|
|
* Return tags as a list of strings separated by given separator
|
|
*
|
|
* @param taskId
|
|
* @return empty string if no tags, otherwise string
|
|
*/
|
|
public String getTagsAsString(long taskId, String separator) {
|
|
StringBuilder tagBuilder = new StringBuilder();
|
|
TodorooCursor<Metadata> tags = getTags(taskId);
|
|
try {
|
|
int length = tags.getCount();
|
|
Metadata metadata = new Metadata();
|
|
for (int i = 0; i < length; i++) {
|
|
tags.moveToNext();
|
|
metadata.readFromCursor(tags);
|
|
tagBuilder.append(metadata.getValue(TAG));
|
|
if (i < length - 1)
|
|
tagBuilder.append(separator);
|
|
}
|
|
} finally {
|
|
tags.close();
|
|
}
|
|
return tagBuilder.toString();
|
|
}
|
|
|
|
/**
|
|
* Save the given array of tags into the database
|
|
* @param taskId
|
|
* @param tags
|
|
*/
|
|
public boolean synchronizeTags(long taskId, LinkedHashSet<String> tags) {
|
|
MetadataService service = PluginServices.getMetadataService();
|
|
|
|
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
|
|
for(String tag : tags) {
|
|
Metadata item = new Metadata();
|
|
item.setValue(Metadata.KEY, KEY);
|
|
item.setValue(TAG, tag);
|
|
metadata.add(item);
|
|
}
|
|
|
|
return service.synchronizeMetadata(taskId, metadata, Metadata.KEY.eq(KEY)) > 0;
|
|
}
|
|
|
|
public int delete(String tag) {
|
|
invalidateTaskCache(tag);
|
|
return PluginServices.getMetadataService().deleteWhere(tagEq(tag, Criterion.all));
|
|
}
|
|
|
|
public int rename(String oldTag, String newTag) {
|
|
// First remove newTag from all tasks that have both oldTag and newTag.
|
|
MetadataService metadataService = PluginServices.getMetadataService();
|
|
metadataService.deleteWhere(
|
|
Criterion.and(
|
|
Metadata.VALUE1.eq(newTag),
|
|
Metadata.TASK.in(rowsWithTag(oldTag, Metadata.TASK))));
|
|
|
|
// Then rename all instances of oldTag to newTag.
|
|
Metadata metadata = new Metadata();
|
|
metadata.setValue(TAG, newTag);
|
|
int ret = metadataService.update(tagEq(oldTag, Criterion.all), metadata);
|
|
invalidateTaskCache(newTag);
|
|
return ret;
|
|
}
|
|
|
|
private Query rowsWithTag(String tag, Field... projections) {
|
|
return Query.select(projections).from(Metadata.TABLE).where(Metadata.VALUE1.eq(tag));
|
|
}
|
|
|
|
private void invalidateTaskCache(String tag) {
|
|
taskService.clearDetails(Task.ID.in(rowsWithTag(tag, Task.ID)));
|
|
Flags.set(Flags.REFRESH);
|
|
}
|
|
|
|
}
|