/** * Copyright (c) 2012 Todoroo Inc * * See the file "LICENSE" for the full license governing this code. */ package com.todoroo.astrid.tags; import java.util.HashMap; import android.content.Context; 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.Query; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.utility.Flags; public class TagCaseMigrator { @Autowired TaskService taskService; @Autowired TagDataService tagDataService; @Autowired MetadataService metadataService; public static final String PREF_SHOW_MIGRATION_ALERT = "tag_case_migration_alert"; //$NON-NLS-1$ private static final String PREF_CASE_MIGRATION_PERFORMED = "tag_case_migration"; //$NON-NLS-1$ public TagCaseMigrator() { DependencyInjectionService.getInstance().inject(this); } private final HashMap<String, String> renameMap = new HashMap<String, String>(); private final HashMap<String, Long> nameToRemoteId = new HashMap<String, Long>(); private final HashMap<String, Integer> nameCountMap = new HashMap<String, Integer>(); public void performTagCaseMigration(@SuppressWarnings("unused") Context context) { if (!Preferences.getBoolean(PREF_CASE_MIGRATION_PERFORMED, false)) { TagService.Tag[] allTagData = TagService.getInstance().getGroupedTags( TagService.GROUPED_TAGS_BY_ALPHA, Criterion.all); boolean shouldShowDialog = false; for (int i = 0; i < allTagData.length - 1; i++) { TagService.Tag first = allTagData[i]; TagService.Tag second = allTagData[i+1]; if (first.tag.equalsIgnoreCase(second.tag)) { shouldShowDialog = true; markForRenaming(first.tag, Long.parseLong(first.uuid)); markForRenaming(second.tag, Long.parseLong(second.uuid)); } } for (String key : renameMap.keySet()) { renameCaseSensitive(key, renameMap.get(key)); updateTagData(key); } Preferences.setBoolean(PREF_CASE_MIGRATION_PERFORMED, true); Preferences.setBoolean(PREF_SHOW_MIGRATION_ALERT, shouldShowDialog); } } private String targetNameForTag(String tag) { String targetName = tag.toLowerCase(); targetName = targetName.substring(0, 1).toUpperCase() + targetName.substring(1); return targetName; } private void markForRenaming(String tag, long remoteId) { if (renameMap.containsKey(tag)) return; String targetName = targetNameForTag(tag); int suffix = 1; if (nameCountMap.containsKey(targetName)) { suffix = nameCountMap.get(targetName); } String newName = targetName + "_" + suffix; //$NON-NLS-1$ nameCountMap.put(targetName, suffix + 1); renameMap.put(tag, newName); nameToRemoteId.put(tag, remoteId); } private void updateTagData(String tag) { long remoteId = nameToRemoteId.get(tag); TodorooCursor<TagData> tagData = tagDataService.query(Query.select(TagData.NAME, TagData.UUID) .where(Criterion.and( TagData.NAME.eq(tag), TagData.UUID.eq(remoteId)))); try { for (tagData.moveToFirst(); !tagData.isAfterLast(); tagData.moveToNext()) { TagData curr = new TagData(tagData); curr.setValue(TagData.NAME, renameMap.get(tag)); tagDataService.save(curr); } } finally { tagData.close(); } addTasksToTargetTag(renameMap.get(tag), targetNameForTag(tag)); } @Deprecated private static Criterion tagEq(String tag, Criterion additionalCriterion) { return Criterion.and( MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_NAME.eq(tag), additionalCriterion); } private void addTasksToTargetTag(String tag, String target) { TodorooCursor<Task> tasks = taskService.query(Query.select(Task.ID).join(Join.inner(Metadata.TABLE, Task.ID.eq(Metadata.TASK))).where(tagEq(tag, Criterion.all))); try { for (tasks.moveToFirst(); !tasks.isAfterLast(); tasks.moveToNext()) { Task curr = new Task(tasks); TodorooCursor<Metadata> tagMetadata = metadataService.query(Query.select(TaskToTagMetadata.TAG_NAME) .where(Criterion.and(TaskToTagMetadata.TAG_NAME.eq(target), Metadata.KEY.eq(TaskToTagMetadata.KEY), Metadata.TASK.eq(curr.getId())))); try { if (tagMetadata.getCount() == 0) { Metadata newTag = new Metadata(); newTag.setValue(Metadata.KEY, TaskToTagMetadata.KEY); newTag.setValue(Metadata.TASK, curr.getId()); newTag.setValue(TaskToTagMetadata.TAG_NAME, target); metadataService.save(newTag); } // else already exists for some weird reason } finally { tagMetadata.close(); } } } finally { tasks.close(); } } @Deprecated private int renameCaseSensitive(String oldTag, String newTag) { // Need this for tag case migration process return renameHelper(oldTag, newTag, true); } @Deprecated private int renameHelper(String oldTag, String newTag, boolean caseSensitive) { // First remove newTag from all tasks that have both oldTag and newTag. 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(TaskToTagMetadata.TAG_NAME, newTag); int ret; if (caseSensitive) ret = metadataService.update(tagEq(oldTag, Criterion.all), metadata); else ret = metadataService.update(TagService.tagEqIgnoreCase(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); } }