diff options
5 files changed, 384 insertions, 15 deletions
diff --git a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/ScheduledTaskContainer.java b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/ScheduledTaskContainer.java index ef3a36547..905051710 100644 --- a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/ScheduledTaskContainer.java +++ b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/ScheduledTaskContainer.java @@ -14,6 +14,7 @@ package org.eclipse.mylyn.internal.tasks.core; import java.util.Calendar; import java.util.Collection; import java.util.HashSet; +import java.util.Iterator; import java.util.Set; import org.eclipse.mylyn.tasks.core.IRepositoryElement; @@ -135,15 +136,45 @@ public class ScheduledTaskContainer extends AbstractTaskContainer { @Override public Collection<ITask> getChildren() { - // TODO: Cache this information until the next modification to pertinent data - + Calendar now = Calendar.getInstance(); + // extend range to include tasks scheduled for this container in different time zones + // timezones range from UTC-12 to UTC+14, but we will ignore UTC+12 to +14 since we can't distinguish them from UTC-12 to -10 + // use minutes to allow for partial hour timezone offsets + int offsetInMinutes = range.getStartDate().getTimeZone().getOffset(now.getTimeInMillis()) / 1000 / 60; + if (offsetInMinutes > 11 * 60) { + // when in UTC+12 to +14, show tasks scheduled in those time zones in the correct bin. This causes tasks scheduled in other time + // zones to show a day late; tasks scheduled in other time zones for WeekDateRanges may not show at all + offsetInMinutes = -(24 * 60 - offsetInMinutes); + } + int minutesForwardToDateline = 11 * 60 - offsetInMinutes; + int minutesBackwardToDateline = 23 * 60 - minutesForwardToDateline; + Calendar start = Calendar.getInstance(); + start.setTimeInMillis(range.getStartDate().getTimeInMillis()); + start.add(Calendar.MINUTE, -minutesForwardToDateline); + Calendar end = Calendar.getInstance(); + end.setTimeInMillis(range.getEndDate().getTimeInMillis()); + end.add(Calendar.MINUTE, minutesBackwardToDateline); + // All tasks scheduled for this date range + Set<ITask> tasks = activityManager.getScheduledTasks(start, end); + if (range instanceof WeekDateRange) { + // remove tasks not scheduled for the week container itself, except for 2 weeks, in which case only remove + // if they will show under future + for (Iterator<ITask> iterator = tasks.iterator(); iterator.hasNext();) { + ITask task = iterator.next(); + if (task instanceof AbstractTask) { + DateRange scheduledDate = ((AbstractTask) task).getScheduledForDate(); + if (!(scheduledDate instanceof WeekDateRange) + && (TaskActivityUtil.getNextWeek().next().compareTo(range) != 0 || scheduledDate.getEndDate() + .after(end))) { + iterator.remove(); + } + } + } + } Set<ITask> children = new HashSet<ITask>(); - Calendar cal = TaskActivityUtil.getCalendar(); - - // All tasks scheduled for this date range - for (ITask task : activityManager.getScheduledTasks(range)) { + for (ITask task : tasks) { if (!task.isCompleted() || isCompletedToday(task)) { if (isDueBeforeScheduled(task) && activityManager.isOwnedByUser(task)) { @@ -168,7 +199,12 @@ public class ScheduledTaskContainer extends AbstractTaskContainer { // Add due tasks if not the This Week container, and not scheduled for earlier date if (!TaskActivityUtil.getCurrentWeek().equals(range) && !TaskActivityUtil.getNextWeek().equals(range)) { - for (ITask task : getTasksDueThisWeek()) { + // tasks are due at the start of a day, so only search in the range of times that correspond to + // the start of the day in some time zone + Calendar endDueSearch = Calendar.getInstance(); + endDueSearch.setTimeInMillis(range.getStartDate().getTimeInMillis()); + endDueSearch.add(Calendar.MINUTE, minutesBackwardToDateline); + for (ITask task : activityManager.getDueTasks(start, endDueSearch)) { if (isScheduledBeforeDue(task)) { continue; } @@ -185,11 +221,19 @@ public class ScheduledTaskContainer extends AbstractTaskContainer { addChild(children, task); } } + // add tasks whose scheduled date starts before today and ends today + Calendar searchFrom = Calendar.getInstance(); + searchFrom.setTimeInMillis(start.getTimeInMillis()); + searchFrom.add(Calendar.DAY_OF_MONTH, -1); + for (ITask task : activityManager.getScheduledTasks(searchFrom, end)) { + if (isScheduledForADay(task)) { + addChild(children, task); + } + } for (ITask task : activityManager.getOverDueTasks()) { addChild(children, task); } - // if not scheduled or due in future, and is active, place in today bin ITask activeTask = activityManager.getActiveTask(); if (activeTask != null && !children.contains(activeTask)) { addChild(children, activeTask); @@ -216,10 +260,6 @@ public class ScheduledTaskContainer extends AbstractTaskContainer { return range instanceof WeekDateRange && ((WeekDateRange) range).isThisWeek(); } - private Set<ITask> getTasksDueThisWeek() { - return activityManager.getDueTasks(range.getStartDate(), range.getEndDate()); - } - private boolean isScheduledForAWeek(ITask task) { return task instanceof AbstractTask && ((AbstractTask) task).getScheduledForDate() instanceof WeekDateRange; } diff --git a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManager.java b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManager.java index 5e7573fd0..b4c77a409 100644 --- a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManager.java +++ b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManager.java @@ -20,6 +20,7 @@ import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -404,11 +405,12 @@ public class TaskActivityManager implements ITaskActivityManager { DateRange endRange = new DateRange(endExclusive); SortedMap<DateRange, Set<ITask>> result = scheduledTasks.subMap(startRange, endRange); - for (DateRange range : result.keySet()) { + for (Entry<DateRange, Set<ITask>> entry : result.entrySet()) { + DateRange range = entry.getKey(); if (start.compareTo(range.getStartDate()) > 0 || end.compareTo(range.getEndDate()) < 0) { continue; } - resultingTasks.addAll(result.get(range)); + resultingTasks.addAll(entry.getValue()); } } return resultingTasks; diff --git a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/WeekDateRange.java b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/WeekDateRange.java index 7c3b398c6..24a471a03 100644 --- a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/WeekDateRange.java +++ b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/WeekDateRange.java @@ -115,7 +115,7 @@ public class WeekDateRange extends DateRange { return this.includes(cal); } - private boolean isWeekAfterNext() { + boolean isWeekAfterNext() { return TaskActivityUtil.getNextWeek().next().compareTo(this) == 0; } diff --git a/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java b/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java index d1ffa1e9e..96a6a9b91 100644 --- a/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java +++ b/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/AllTasksTests.java @@ -29,6 +29,7 @@ import org.eclipse.mylyn.tasks.tests.data.TaskDataExternalizerTest; import org.eclipse.mylyn.tasks.tests.data.Xml11InputStreamTest; import org.eclipse.mylyn.tasks.tests.ui.AbstractRepositoryConnectorUiTest; import org.eclipse.mylyn.tasks.tests.ui.MultipleTaskHyperlinkDetectorTest; +import org.eclipse.mylyn.tasks.tests.ui.ScheduledTaskContainerTest; import org.eclipse.mylyn.tasks.tests.ui.TaskAttachmentPropertyTesterTest; import org.eclipse.mylyn.tasks.tests.ui.TaskHyperlinkDetectorTest; import org.eclipse.mylyn.tasks.tests.ui.TaskListSynchronizationSchedulerTest; @@ -137,6 +138,7 @@ public class AllTasksTests { suite.addTestSuite(AbstractRepositoryConnectorUiTest.class); suite.addTestSuite(SynchronizeTasksJobTest.class); suite.addTestSuite(TaskAttributeTest.class); + suite.addTestSuite(ScheduledTaskContainerTest.class); return suite; } diff --git a/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/ui/ScheduledTaskContainerTest.java b/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/ui/ScheduledTaskContainerTest.java new file mode 100644 index 000000000..916ffa156 --- /dev/null +++ b/org.eclipse.mylyn.tasks.tests/src/org/eclipse/mylyn/tasks/tests/ui/ScheduledTaskContainerTest.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.tasks.tests.ui; + +import java.util.Calendar; +import java.util.TimeZone; + +import junit.framework.TestCase; + +import org.eclipse.mylyn.internal.tasks.core.AbstractTask; +import org.eclipse.mylyn.internal.tasks.core.DateRange; +import org.eclipse.mylyn.internal.tasks.core.DayDateRange; +import org.eclipse.mylyn.internal.tasks.core.LocalTask; +import org.eclipse.mylyn.internal.tasks.core.ScheduledTaskContainer; +import org.eclipse.mylyn.internal.tasks.core.TaskActivityManager; +import org.eclipse.mylyn.internal.tasks.core.TaskActivityUtil; +import org.eclipse.mylyn.internal.tasks.core.WeekDateRange; +import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin; +import org.eclipse.mylyn.tasks.core.ITask; +import org.eclipse.mylyn.tasks.core.TaskRepository; +import org.eclipse.mylyn.tasks.tests.TaskTestUtil; +import org.eclipse.mylyn.tasks.tests.connector.MockRepositoryConnector; + +/** + * @author Sam Davis + */ +public class ScheduledTaskContainerTest extends TestCase { + private TaskActivityManager taskActivityManager; + + private long taskID; + + private TaskRepository repository; + + private final long instant = 1334355143000L;// arbitrary fixed point in time + + @Override + protected void setUp() throws Exception { + taskActivityManager = TasksUiPlugin.getTaskActivityManager(); + TaskTestUtil.resetTaskListAndRepositories(); + repository = new TaskRepository(MockRepositoryConnector.CONNECTOR_KIND, MockRepositoryConnector.REPOSITORY_URL); + TasksUiPlugin.getRepositoryManager().addRepository(repository); + } + + @Override + protected void tearDown() throws Exception { + taskActivityManager.clear(); + } + + public void testUSHawaiiDay() { + runDayTest("US/Hawaii"); + } + + public void testCanadaPacificDay() { + runDayTest("Canada/Pacific"); + } + + public void testBrazilWestDay() { + runDayTest("Brazil/West"); + } + + public void testEuropeDublinDay() { + runDayTest("Europe/Dublin"); + } + + public void testGreenwichDay() { + runDayTest("Greenwich"); + } + + public void testPortugalDay() { + runDayTest("Portugal"); + } + + public void testUniversalDay() { + runDayTest("Universal"); + } + + public void testEuropeViennaDay() { + runDayTest("Europe/Vienna"); + } + + public void testAsiaIstanbulDay() { + runDayTest("Asia/Istanbul"); + } + + public void testAsiaDubaiDay() { + runDayTest("Asia/Dubai"); + } + + public void testIndianComoroDay() { + runDayTest("Indian/Comoro"); + } + + public void testAsiaMacauDay() { + runDayTest("Asia/Macau"); + } + + public void testAustraliaSouthDay() { + runDayTest("Australia/South"); + } + + public void testAsiaTokyoDay() { + runDayTest("Asia/Tokyo"); + } + +// public void testPacificFijiDay() { +// // Fails because we cannot distinguish UTC+12 to UTC+14 from UTC-12 to -10 +// runDayTest("Pacific/Fiji"); +// } + + public void testUSHawaiiWeek() { + runWeekTest("US/Hawaii"); + } + + public void testCanadaPacificWeek() { + runWeekTest("Canada/Pacific"); + } + + public void testBrazilWestWeek() { + runWeekTest("Brazil/West"); + } + + public void testEuropeDublinWeek() { + runWeekTest("Europe/Dublin"); + } + + public void testGreenwichWeek() { + runWeekTest("Greenwich"); + } + + public void testPortugalWeek() { + runWeekTest("Portugal"); + } + + public void testUniversalWeek() { + runWeekTest("Universal"); + } + + public void testEuropeViennaWeek() { + runWeekTest("Europe/Vienna"); + } + + public void testAsiaIstanbulWeek() { + runWeekTest("Asia/Istanbul"); + } + + public void testAsiaDubaiWeek() { + runWeekTest("Asia/Dubai"); + } + + public void testIndianComoroWeek() { + runWeekTest("Indian/Comoro"); + } + + public void testAsiaMacauWeek() { + runWeekTest("Asia/Macau"); + } + + public void testAustraliaSouthWeek() { + runWeekTest("Australia/South"); + } + + public void testAsiaTokyoWeek() { + runWeekTest("Asia/Tokyo"); + } + +// public void testPacificFijiWeek() { +// // Fails because we cannot distinguish UTC+12 to UTC+14 from UTC-12 to -10 +// runWeekTest("Pacific/Fiji"); +// } + + /** + * Test day bins + */ + protected void runDayTest(String localTimezone) { + // create scheduled tasks + ITask taskUSHawaii = createTaskScheduledForDay(instant, "US/Hawaii"); + ITask taskCanadaPacific = createTaskScheduledForDay(instant, "Canada/Pacific"); + ITask taskCanadaEastern = createTaskScheduledForDay(instant, "Canada/Eastern"); + ITask taskEuropeWarsaw = createTaskScheduledForDay(instant, "Europe/Warsaw"); + ITask taskIndianComoro = createTaskScheduledForDay(instant, "Indian/Comoro"); + ITask taskAsiaTokyo = createTaskScheduledForDay(instant, "Asia/Tokyo"); + ITask taskAustraliaSydney = createTaskScheduledForDay(instant, "Australia/Sydney"); + // create due tasks + ITask taskUSHawaiiDue = createTaskDueForDay(instant, "US/Hawaii"); + ITask taskCanadaPacificDue = createTaskDueForDay(instant, "Canada/Pacific"); + ITask taskCanadaEasternDue = createTaskDueForDay(instant, "Canada/Eastern"); + ITask taskEuropeWarsawDue = createTaskDueForDay(instant, "Europe/Warsaw"); + ITask taskIndianComoroDue = createTaskDueForDay(instant, "Indian/Comoro"); + ITask taskAsiaTokyoDue = createTaskDueForDay(instant, "Asia/Tokyo"); + ITask taskAustraliaSydneyDue = createTaskDueForDay(instant, "Australia/Sydney"); + ScheduledTaskContainer dayContainer = new ScheduledTaskContainer(taskActivityManager, getDayOf(instant, + localTimezone)); + // check scheduled tasks are contained + assertTrue(dayContainer.getChildren().contains(taskUSHawaii)); + assertTrue(dayContainer.getChildren().contains(taskCanadaPacific)); + assertTrue(dayContainer.getChildren().contains(taskCanadaEastern)); + assertTrue(dayContainer.getChildren().contains(taskEuropeWarsaw)); + assertTrue(dayContainer.getChildren().contains(taskIndianComoro)); + assertTrue(dayContainer.getChildren().contains(taskAsiaTokyo)); + assertTrue(dayContainer.getChildren().contains(taskAustraliaSydney)); + // check due tasks are contained + assertTrue(dayContainer.getChildren().contains(taskUSHawaiiDue)); + assertTrue(dayContainer.getChildren().contains(taskCanadaPacificDue)); + assertTrue(dayContainer.getChildren().contains(taskCanadaEasternDue)); + assertTrue(dayContainer.getChildren().contains(taskEuropeWarsawDue)); + assertTrue(dayContainer.getChildren().contains(taskIndianComoroDue)); + assertTrue(dayContainer.getChildren().contains(taskAsiaTokyoDue)); + assertTrue(dayContainer.getChildren().contains(taskAustraliaSydneyDue)); + // surrounding days should be empty + DayDateRange previousDay = getDayOf(instant, localTimezone); + snapForwardNumDays(previousDay, -1); + ScheduledTaskContainer previousDayContainer = new ScheduledTaskContainer(taskActivityManager, previousDay); + assertTrue(previousDayContainer.getChildren().isEmpty()); + DayDateRange nextDay = getDayOf(instant, localTimezone); + snapForwardNumDays(nextDay, 1); + ScheduledTaskContainer nextDayContainer = new ScheduledTaskContainer(taskActivityManager, nextDay); + assertTrue(nextDayContainer.getChildren().isEmpty()); + } + + /** + * test week bins + */ + protected void runWeekTest(String localTimezone) { + ITask taskUSHawaii = createTaskScheduledForWeek(instant, "US/Hawaii"); + ITask taskCanadaPacific = createTaskScheduledForWeek(instant, "Canada/Pacific"); + ITask taskCanadaEastern = createTaskScheduledForWeek(instant, "Canada/Eastern"); + ITask taskEuropeWarsaw = createTaskScheduledForWeek(instant, "Europe/Warsaw"); + ITask taskIndianComoro = createTaskScheduledForWeek(instant, "Indian/Comoro"); + ITask taskAsiaTokyo = createTaskScheduledForWeek(instant, "Asia/Tokyo"); + ITask taskAustraliaSydney = createTaskScheduledForWeek(instant, "Australia/Sydney"); + ScheduledTaskContainer weekContainer = new ScheduledTaskContainer(taskActivityManager, getWeekOf(instant, + localTimezone)); + assertTrue(weekContainer.getChildren().contains(taskUSHawaii)); + assertTrue(weekContainer.getChildren().contains(taskCanadaPacific)); + assertTrue(weekContainer.getChildren().contains(taskCanadaEastern)); + assertTrue(weekContainer.getChildren().contains(taskEuropeWarsaw)); + assertTrue(weekContainer.getChildren().contains(taskIndianComoro)); + assertTrue(weekContainer.getChildren().contains(taskAsiaTokyo)); + assertTrue(weekContainer.getChildren().contains(taskAustraliaSydney)); + // surrounding days should be empty + DayDateRange previousDay = TaskActivityUtil.getDayOf(weekContainer.getDateRange().getStartDate().getTime()); + snapForwardNumDays(previousDay, -1); + ScheduledTaskContainer previousDayContainer = new ScheduledTaskContainer(taskActivityManager, previousDay); + assertTrue(previousDayContainer.getChildren().isEmpty()); + DayDateRange nextDay = TaskActivityUtil.getDayOf(weekContainer.getDateRange().getEndDate().getTime()); + snapForwardNumDays(nextDay, 1); + ScheduledTaskContainer nextDayContainer = new ScheduledTaskContainer(taskActivityManager, nextDay); + assertTrue(nextDayContainer.getChildren().isEmpty()); + // surrounding weeks should be empty + WeekDateRange previousWeek = TaskActivityUtil.getWeekOf(previousDay.getStartDate().getTime()); + ScheduledTaskContainer previousWeekContainer = new ScheduledTaskContainer(taskActivityManager, previousWeek); + assertTrue(previousWeekContainer.getChildren().isEmpty()); + WeekDateRange nextWeek = TaskActivityUtil.getWeekOf(nextDay.getStartDate().getTime()); + ScheduledTaskContainer nextWeekContainer = new ScheduledTaskContainer(taskActivityManager, nextWeek); + assertTrue(nextWeekContainer.getChildren().isEmpty()); + } + + protected void snapForwardNumDays(DayDateRange previousDay, int num) { + previousDay.getStartDate().add(Calendar.DAY_OF_MONTH, num); + previousDay.getEndDate().add(Calendar.DAY_OF_MONTH, num); + } + + protected AbstractTask createTaskScheduledForDay(long date, String timezone) { + AbstractTask task = new LocalTask(taskID++ + "", "task " + taskID); + taskActivityManager.setScheduledFor(task, getDayOf(date, timezone)); + return task; + } + + protected AbstractTask createTaskDueForDay(long date, String timezone) { + AbstractTask task = new LocalTask(taskID++ + "", "task " + taskID); + taskActivityManager.setDueDate(task, getDayOf(date, timezone).getStartDate().getTime()); + return task; + } + + protected AbstractTask createTaskScheduledForWeek(long date, String timezone) { + AbstractTask task = new LocalTask(taskID++ + "", "task " + taskID); + taskActivityManager.setScheduledFor(task, getWeekOf(date, timezone)); + return task; + } + + protected DateRange getWeekOf(long date, String timezoneString) { + TimeZone timeZone = TimeZone.getTimeZone(timezoneString); + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(date); + Calendar start = getSameDayInTimeZone(cal, timeZone); + TaskActivityUtil.snapStartOfWorkWeek(start); + Calendar end = getSameDayInTimeZone(cal, timeZone); + TaskActivityUtil.snapEndOfWeek(end); + WeekDateRange weekInTimezone = new WeekDateRange(start, end); + return weekInTimezone; + } + + protected DayDateRange getDayOf(long date, String timezoneString) { + TimeZone timeZone = TimeZone.getTimeZone(timezoneString); + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(date); + Calendar start = getSameDayInTimeZone(cal, timeZone); + Calendar end = getSameDayInTimeZone(cal, timeZone); + TaskActivityUtil.snapEndOfDay(end); + DayDateRange dayInTimezone = new DayDateRange(start, end); + return dayInTimezone; + } + + /** + * use the same y/m/d values but ignore the time + */ + protected Calendar getSameDayInTimeZone(Calendar cal, TimeZone timeZone) { + Calendar day = Calendar.getInstance(timeZone); + day.set(Calendar.YEAR, cal.get(Calendar.YEAR)); + day.set(Calendar.MONTH, cal.get(Calendar.MONTH)); + day.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH)); + TaskActivityUtil.snapStartOfDay(day); + return day; + } + +}
\ No newline at end of file |