diff --git a/astrid/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.java b/astrid/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.java index 509df5f52..b1e645445 100755 --- a/astrid/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.java +++ b/astrid/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.java @@ -49,6 +49,7 @@ import com.todoroo.astrid.actfm.ActFmCameraModule; import com.todoroo.astrid.actfm.ActFmCameraModule.CameraResultCallback; import com.todoroo.astrid.alarms.AlarmService; import com.todoroo.astrid.dao.MetadataDao; +import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TaskAttachmentDao; import com.todoroo.astrid.dao.UserActivityDao; import com.todoroo.astrid.data.RemoteModel; @@ -175,6 +176,7 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener { @Inject GCalHelper gcalHelper; @Inject ActivityPreferences preferences; @Inject DateChangedAlerts dateChangedAlerts; + @Inject TagDataDao tagDataDao; // --- UI components @@ -360,7 +362,7 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener { timerAction = new TimerActionControlSet(notificationManager, taskService, getActivity(), getView()); controls.add(timerAction); - TagsControlSet tagsControlSet = new TagsControlSet(preferences, tagService, getActivity()); + TagsControlSet tagsControlSet = new TagsControlSet(metadataDao, tagDataDao, preferences, tagService, getActivity()); controls.add(tagsControlSet); controlSetMap.put(getString(R.string.TEA_ctrl_lists_pref), tagsControlSet); diff --git a/astrid/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.java b/astrid/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.java index 42e6f87b3..12e63adaa 100644 --- a/astrid/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.java +++ b/astrid/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.java @@ -18,10 +18,12 @@ import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.core.SortHelper; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; +import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService.Tag; +import com.todoroo.astrid.tags.TaskToTagMetadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -174,7 +176,7 @@ public class Astrid2TaskProvider extends InjectingContentProvider { cursor.moveToNext(); Task task = new Task(cursor); - String taskTags = tagService.get().getTagsAsString(task.getId(), TAG_SEPARATOR); + String taskTags = getTagsAsString(task.getId(), TAG_SEPARATOR); Object[] values = new Object[7]; values[0] = task.getTitle(); @@ -263,4 +265,27 @@ public class Astrid2TaskProvider extends InjectingContentProvider { log.error(e.getMessage(), e); } } + + /** + * Return tags as a list of strings separated by given separator + * @return empty string if no tags, otherwise string + */ + private String getTagsAsString(long taskId, String separator) { + StringBuilder tagBuilder = new StringBuilder(); + TodorooCursor tags = tagService.get().getTags(taskId); + try { + int length = tags.getCount(); + for (int i = 0; i < length; i++) { + tags.moveToNext(); + Metadata metadata = new Metadata(tags); + tagBuilder.append(metadata.getValue(TaskToTagMetadata.TAG_NAME)); + if (i < length - 1) { + tagBuilder.append(separator); + } + } + } finally { + tags.close(); + } + return tagBuilder.toString(); + } } diff --git a/astrid/src/main/java/com/todoroo/astrid/service/TaskService.java b/astrid/src/main/java/com/todoroo/astrid/service/TaskService.java index b88f44b1f..47b501dc5 100644 --- a/astrid/src/main/java/com/todoroo/astrid/service/TaskService.java +++ b/astrid/src/main/java/com/todoroo/astrid/service/TaskService.java @@ -16,9 +16,11 @@ import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.api.PermaSql; import com.todoroo.astrid.dao.MetadataDao; +import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.RemoteModel; +import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TaskToTagMetadata; @@ -59,6 +61,7 @@ public class TaskService { public static final String TRANS_REPEAT_COMPLETE = "repeat-complete"; //$NON-NLS-1$ + private final TagDataDao tagDataDao; private final TaskDao taskDao; private final Broadcaster broadcaster; private final FilterCounter filterCounter; @@ -67,8 +70,9 @@ public class TaskService { private final MetadataDao metadataDao; @Inject - public TaskService(TaskDao taskDao, Broadcaster broadcaster, FilterCounter filterCounter, + public TaskService(TagDataDao tagDataDao, TaskDao taskDao, Broadcaster broadcaster, FilterCounter filterCounter, RefreshScheduler refreshScheduler, TagService tagService, MetadataDao metadataDao) { + this.tagDataDao = tagDataDao; this.taskDao = taskDao; this.broadcaster = broadcaster; this.filterCounter = filterCounter; @@ -218,7 +222,7 @@ public class TaskService { private void quickAdd(Task task, List tags) { saveWithoutPublishingFilterUpdate(task); for(String tag : tags) { - tagService.createLink(task, tag); + createLink(task, tag); } broadcastFilterListUpdated(); } @@ -301,10 +305,10 @@ public class TaskService { if (TaskToTagMetadata.KEY.equals(metadata.getKey())) { if (metadata.containsNonNullValue(TaskToTagMetadata.TAG_UUID) && !RemoteModel.NO_UUID.equals(metadata.getValue(TaskToTagMetadata.TAG_UUID))) { // This is more efficient - tagService.createLink(task, metadata.getValue(TaskToTagMetadata.TAG_NAME), metadata.getValue(TaskToTagMetadata.TAG_UUID)); + createLink(task, metadata.getValue(TaskToTagMetadata.TAG_NAME), metadata.getValue(TaskToTagMetadata.TAG_UUID)); } else { // This is necessary for backwards compatibility - tagService.createLink(task, metadata.getValue(TaskToTagMetadata.TAG_NAME)); + createLink(task, metadata.getValue(TaskToTagMetadata.TAG_NAME)); } } else { metadataDao.persist(metadata); @@ -313,4 +317,22 @@ public class TaskService { return task; } + + private void createLink(Task task, String tagName) { + TagData tagData = tagDataDao.getTagByName(tagName, TagData.NAME, TagData.UUID); + if (tagData == null) { + tagData = new TagData(); + tagData.setName(tagName); + tagDataDao.persist(tagData); + } + createLink(task, tagData.getName(), tagData.getUUID()); + } + + private void createLink(Task task, String tagName, String tagUuid) { + Metadata link = TaskToTagMetadata.newTagMetadata(task.getId(), task.getUuid(), tagName, tagUuid); + if (metadataDao.update(Criterion.and(MetadataDao.MetadataCriteria.byTaskAndwithKey(task.getId(), TaskToTagMetadata.KEY), + TaskToTagMetadata.TASK_UUID.eq(task.getUUID()), TaskToTagMetadata.TAG_UUID.eq(tagUuid)), link) <= 0) { + metadataDao.createNew(link); + } + } } diff --git a/astrid/src/main/java/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java b/astrid/src/main/java/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java index 313f73084..f7d56a5ce 100644 --- a/astrid/src/main/java/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java +++ b/astrid/src/main/java/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java @@ -31,6 +31,12 @@ import javax.inject.Inject; public class TagCustomFilterCriteriaExposer extends InjectingBroadcastReceiver { + private static int[] default_tag_images = new int[] { + R.drawable.default_list_0, + R.drawable.default_list_1, + R.drawable.default_list_2, + R.drawable.default_list_3 + }; private static final String IDENTIFIER_TAG_IS = "tag_is"; //$NON-NLS-1$ private static final String IDENTIFIER_TAG_CONTAINS = "tag_contains"; //$NON-NLS-1$ @@ -65,7 +71,7 @@ public class TagCustomFilterCriteriaExposer extends InjectingBroadcastReceiver { MetadataDao.MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_NAME.eq("?"), Metadata.DELETION_DATE.eq(0))).toString(), values, tagNames, tagNames, - ((BitmapDrawable)r.getDrawable(TagService.getDefaultImageIDForTag(RemoteModel.NO_UUID))).getBitmap(), + ((BitmapDrawable)r.getDrawable(getDefaultImageIDForTag(RemoteModel.NO_UUID))).getBitmap(), context.getString(R.string.CFC_tag_name)); ret[j++] = criterion; } @@ -81,7 +87,7 @@ public class TagCustomFilterCriteriaExposer extends InjectingBroadcastReceiver { MetadataDao.MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_NAME.like("%?%"), Metadata.DELETION_DATE.eq(0))).toString(), context.getString(R.string.CFC_tag_contains_name), "", - ((BitmapDrawable)r.getDrawable(TagService.getDefaultImageIDForTag(RemoteModel.NO_UUID))).getBitmap(), + ((BitmapDrawable)r.getDrawable(getDefaultImageIDForTag(RemoteModel.NO_UUID))).getBitmap(), context.getString(R.string.CFC_tag_contains_name)); ret[j] = criterion; } @@ -92,4 +98,12 @@ public class TagCustomFilterCriteriaExposer extends InjectingBroadcastReceiver { context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } + + private static int getDefaultImageIDForTag(String nameOrUUID) { + if (RemoteModel.NO_UUID.equals(nameOrUUID)) { + int random = (int)(Math.random()*4); + return default_tag_images[random]; + } + return default_tag_images[(Math.abs(nameOrUUID.hashCode()))%4]; + } } diff --git a/astrid/src/main/java/com/todoroo/astrid/tags/TagFilterExposer.java b/astrid/src/main/java/com/todoroo/astrid/tags/TagFilterExposer.java index c65dd7665..043102641 100644 --- a/astrid/src/main/java/com/todoroo/astrid/tags/TagFilterExposer.java +++ b/astrid/src/main/java/com/todoroo/astrid/tags/TagFilterExposer.java @@ -16,6 +16,7 @@ import android.text.TextUtils; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.sql.Criterion; +import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.astrid.actfm.TagViewFragment; import com.todoroo.astrid.api.AstridApiConstants; @@ -24,9 +25,11 @@ import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterWithCustomIntent; import com.todoroo.astrid.api.FilterWithUpdate; +import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.TagData; +import com.todoroo.astrid.data.Task; import com.todoroo.astrid.tags.TagService.Tag; import org.tasks.R; @@ -134,7 +137,7 @@ public class TagFilterExposer extends InjectingBroadcastReceiver implements Astr if (shouldAddUntagged) { Filter untagged = new Filter(r.getString(R.string.tag_FEx_untagged), r.getString(R.string.tag_FEx_untagged), - tagService.untaggedTemplate(), + untaggedTemplate(), null); filters.add(untagged); } @@ -159,4 +162,11 @@ public class TagFilterExposer extends InjectingBroadcastReceiver implements Astr return prepareFilters(); } + private QueryTemplate untaggedTemplate() { + return new QueryTemplate().where(Criterion.and( + Criterion.not(Task.UUID.in(Query.select(TaskToTagMetadata.TASK_UUID).from(Metadata.TABLE) + .where(Criterion.and(MetadataDao.MetadataCriteria.withKey(TaskToTagMetadata.KEY), Metadata.DELETION_DATE.eq(0))))), + TaskCriteria.isActive(), + TaskCriteria.isVisible())); + } } diff --git a/astrid/src/main/java/com/todoroo/astrid/tags/TagService.java b/astrid/src/main/java/com/todoroo/astrid/tags/TagService.java index d27310334..34602f744 100644 --- a/astrid/src/main/java/com/todoroo/astrid/tags/TagService.java +++ b/astrid/src/main/java/com/todoroo/astrid/tags/TagService.java @@ -17,22 +17,15 @@ 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.andlib.utility.DateUtilities; import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.TagDataDao; -import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.data.Metadata; -import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.Task; -import org.tasks.R; - import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; @@ -46,13 +39,6 @@ import javax.inject.Singleton; @Singleton public final class TagService { - private static int[] default_tag_images = new int[] { - R.drawable.default_list_0, - R.drawable.default_list_1, - R.drawable.default_list_2, - R.drawable.default_list_3 - }; - private final MetadataDao metadataDao; private final TagDataDao tagDataDao; @@ -74,6 +60,7 @@ public final class TagService { * @author Tim Su * */ + // TODO: get rid of this public static final class Tag { public String tag; public String uuid; @@ -100,21 +87,6 @@ public final class TagService { return new QueryTemplate().join(Join.inner(Metadata.TABLE.as("mtags"), Task.UUID.eq(Field.field("mtags." + TaskToTagMetadata.TASK_UUID.name)))) .where(fullCriterion); } - - } - - public static Criterion tagEqIgnoreCase(String tag, Criterion additionalCriterion) { - return Criterion.and( - MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_NAME.eqCaseInsensitive(tag), - additionalCriterion); - } - - public QueryTemplate untaggedTemplate() { - return new QueryTemplate().where(Criterion.and( - Criterion.not(Task.UUID.in(Query.select(TaskToTagMetadata.TASK_UUID).from(Metadata.TABLE) - .where(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), Metadata.DELETION_DATE.eq(0))))), - TaskCriteria.isActive(), - TaskCriteria.isVisible())); } /** @@ -151,42 +123,6 @@ public final class TagService { return tagData == null ? null : new Tag(tagData); } - public void createLink(Task task, String tagName) { - TagData tagData = tagDataDao.getTagByName(tagName, TagData.NAME, TagData.UUID); - if (tagData == null) { - tagData = new TagData(); - tagData.setName(tagName); - tagDataDao.persist(tagData); - } - createLink(task, tagData.getName(), tagData.getUUID()); - } - - public void createLink(Task task, String tagName, String tagUuid) { - Metadata link = TaskToTagMetadata.newTagMetadata(task.getId(), task.getUuid(), tagName, tagUuid); - if (metadataDao.update(Criterion.and(MetadataCriteria.byTaskAndwithKey(task.getId(), TaskToTagMetadata.KEY), - TaskToTagMetadata.TASK_UUID.eq(task.getUUID()), TaskToTagMetadata.TAG_UUID.eq(tagUuid)), link) <= 0) { - metadataDao.createNew(link); - } - } - - /** - * Delete all links between the specified task and the list of tags - */ - public void deleteLinks(long taskId, String taskUuid, String[] tagUuids) { - Metadata deleteTemplate = new Metadata(); - deleteTemplate.setTask(taskId); // Need this for recording changes in outstanding table - deleteTemplate.setDeletionDate(DateUtilities.now()); - if (tagUuids != null) { - for (String uuid : tagUuids) { - // TODO: Right now this is in a loop because each deleteTemplate needs the individual tagUuid in order to record - // the outstanding entry correctly. If possible, this should be improved to a single query - deleteTemplate.setValue(TaskToTagMetadata.TAG_UUID, uuid); // Need this for recording changes in outstanding table - metadataDao.update(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), Metadata.DELETION_DATE.eq(0), - TaskToTagMetadata.TASK_UUID.eq(taskUuid), TaskToTagMetadata.TAG_UUID.eq(uuid)), deleteTemplate); - } - } - } - /** * Return tags on the given task * @return cursor. PLEASE CLOSE THE CURSOR! @@ -199,29 +135,6 @@ public final class TagService { return metadataDao.query(query); } - /** - * Return tags as a list of strings separated by given separator - * @return empty string if no tags, otherwise string - */ - public String getTagsAsString(long taskId, String separator) { - StringBuilder tagBuilder = new StringBuilder(); - TodorooCursor tags = getTags(taskId); - try { - int length = tags.getCount(); - for (int i = 0; i < length; i++) { - tags.moveToNext(); - Metadata metadata = new Metadata(tags); - tagBuilder.append(metadata.getValue(TaskToTagMetadata.TAG_NAME)); - if (i < length - 1) { - tagBuilder.append(separator); - } - } - } finally { - tags.close(); - } - return tagBuilder.toString(); - } - /** * Return all tags (including metadata tags and TagData tags) in an array list */ @@ -238,41 +151,6 @@ public final class TagService { return tagList; } - /** - * Save the given array of tags into the database - */ - public void synchronizeTags(long taskId, String taskUuid, Set tags) { - HashSet existingLinks = new HashSet<>(); - TodorooCursor links = metadataDao.query(Query.select(Metadata.PROPERTIES) - .where(Criterion.and(TaskToTagMetadata.TASK_UUID.eq(taskUuid), Metadata.DELETION_DATE.eq(0)))); - try { - for (links.moveToFirst(); !links.isAfterLast(); links.moveToNext()) { - Metadata link = new Metadata(links); - existingLinks.add(link.getValue(TaskToTagMetadata.TAG_UUID)); - } - } finally { - links.close(); - } - - for (String tag : tags) { - TagData tagData = tagDataDao.getTagByName(tag, TagData.NAME, TagData.UUID); - if (tagData == null) { - tagData = new TagData(); - tagData.setName(tag); - tagDataDao.persist(tagData); - } - if (existingLinks.contains(tagData.getUUID())) { - existingLinks.remove(tagData.getUUID()); - } else { - Metadata newLink = TaskToTagMetadata.newTagMetadata(taskId, taskUuid, tag, tagData.getUUID()); - metadataDao.createNew(newLink); - } - } - - // Mark as deleted links that don't exist anymore - deleteLinks(taskId, taskUuid, existingLinks.toArray(new String[existingLinks.size()])); - } - /** * If a tag already exists in the database that case insensitively matches the * given tag, return that. Otherwise, return the argument @@ -297,6 +175,12 @@ public final class TagService { return tagWithCase; } + private static Criterion tagEqIgnoreCase(String tag, Criterion additionalCriterion) { + return Criterion.and( + MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_NAME.eqCaseInsensitive(tag), + additionalCriterion); + } + public int rename(String uuid, String newName) { TagData template = new TagData(); template.setName(newName); @@ -307,12 +191,4 @@ public final class TagService { return metadataDao.update(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_UUID.eq(uuid)), metadataTemplate); } - - public static int getDefaultImageIDForTag(String nameOrUUID) { - if (RemoteModel.NO_UUID.equals(nameOrUUID)) { - int random = (int)(Math.random()*4); - return default_tag_images[random]; - } - return default_tag_images[(Math.abs(nameOrUUID.hashCode()))%4]; - } } diff --git a/astrid/src/main/java/com/todoroo/astrid/tags/TagsControlSet.java b/astrid/src/main/java/com/todoroo/astrid/tags/TagsControlSet.java index 2c6903228..2aa50de3d 100644 --- a/astrid/src/main/java/com/todoroo/astrid/tags/TagsControlSet.java +++ b/astrid/src/main/java/com/todoroo/astrid/tags/TagsControlSet.java @@ -25,8 +25,13 @@ import android.widget.TextView.OnEditorActionListener; import com.todoroo.andlib.data.AbstractModel; import com.todoroo.andlib.data.TodorooCursor; +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.TagDataDao; import com.todoroo.astrid.data.Metadata; +import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.tags.TagService.Tag; import com.todoroo.astrid.ui.PopupControlSet; @@ -37,8 +42,10 @@ import org.tasks.preferences.ActivityPreferences; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import static org.tasks.preferences.ResourceResolver.getResource; @@ -65,10 +72,14 @@ public final class TagsControlSet extends PopupControlSet { //private final LinearLayout tagsContainer; private final TextView tagsDisplay; + private final MetadataDao metadataDao; + private final TagDataDao tagDataDao; private final TagService tagService; - public TagsControlSet(ActivityPreferences preferences, TagService tagService, Activity activity) { + public TagsControlSet(MetadataDao metadataDao, TagDataDao tagDataDao, ActivityPreferences preferences, TagService tagService, Activity activity) { super(preferences, activity, R.layout.control_set_tags, R.layout.control_set_default_display, R.string.TEA_tags_label_long); + this.metadataDao = metadataDao; + this.tagDataDao = tagDataDao; this.tagService = tagService; tagsDisplay = (TextView) getDisplayView().findViewById(R.id.display_row_edit); image = (ImageView) getDisplayView().findViewById(R.id.display_row_icon); @@ -308,7 +319,7 @@ public final class TagsControlSet extends PopupControlSet { LinkedHashSet tags = getTagSet(); - tagService.synchronizeTags(task.getId(), task.getUUID(), tags); + synchronizeTags(task.getId(), task.getUUID(), tags); Flags.set(Flags.TAGS_CHANGED); task.setModificationDate(DateUtilities.now()); } @@ -326,4 +337,57 @@ public final class TagsControlSet extends PopupControlSet { image.setImageResource(R.drawable.tea_icn_lists_gray); } } + + /** + * Save the given array of tags into the database + */ + private void synchronizeTags(long taskId, String taskUuid, Set tags) { + HashSet existingLinks = new HashSet<>(); + TodorooCursor links = metadataDao.query(Query.select(Metadata.PROPERTIES) + .where(Criterion.and(TaskToTagMetadata.TASK_UUID.eq(taskUuid), Metadata.DELETION_DATE.eq(0)))); + try { + for (links.moveToFirst(); !links.isAfterLast(); links.moveToNext()) { + Metadata link = new Metadata(links); + existingLinks.add(link.getValue(TaskToTagMetadata.TAG_UUID)); + } + } finally { + links.close(); + } + + for (String tag : tags) { + TagData tagData = tagDataDao.getTagByName(tag, TagData.NAME, TagData.UUID); + if (tagData == null) { + tagData = new TagData(); + tagData.setName(tag); + tagDataDao.persist(tagData); + } + if (existingLinks.contains(tagData.getUUID())) { + existingLinks.remove(tagData.getUUID()); + } else { + Metadata newLink = TaskToTagMetadata.newTagMetadata(taskId, taskUuid, tag, tagData.getUUID()); + metadataDao.createNew(newLink); + } + } + + // Mark as deleted links that don't exist anymore + deleteLinks(taskId, taskUuid, existingLinks.toArray(new String[existingLinks.size()])); + } + + /** + * Delete all links between the specified task and the list of tags + */ + private void deleteLinks(long taskId, String taskUuid, String[] tagUuids) { + Metadata deleteTemplate = new Metadata(); + deleteTemplate.setTask(taskId); // Need this for recording changes in outstanding table + deleteTemplate.setDeletionDate(DateUtilities.now()); + if (tagUuids != null) { + for (String uuid : tagUuids) { + // TODO: Right now this is in a loop because each deleteTemplate needs the individual tagUuid in order to record + // the outstanding entry correctly. If possible, this should be improved to a single query + deleteTemplate.setValue(TaskToTagMetadata.TAG_UUID, uuid); // Need this for recording changes in outstanding table + metadataDao.update(Criterion.and(MetadataDao.MetadataCriteria.withKey(TaskToTagMetadata.KEY), Metadata.DELETION_DATE.eq(0), + TaskToTagMetadata.TASK_UUID.eq(taskUuid), TaskToTagMetadata.TAG_UUID.eq(uuid)), deleteTemplate); + } + } + } }