Make filtered MemoryHabitLists update automatically

pull/312/head
Alinson S. Xavier 8 years ago
parent 71fe6137be
commit 00660d3e36

@ -41,7 +41,7 @@ public class SQLiteRepository<T>
@NonNull @NonNull
private final SQLiteDatabase db; private final SQLiteDatabase db;
public SQLiteRepository(@NonNull Class klass, @NonNull SQLiteDatabase db) public SQLiteRepository(@NonNull Class<T> klass, @NonNull SQLiteDatabase db)
{ {
this.klass = klass; this.klass = klass;
this.db = db; this.db = db;

@ -37,6 +37,8 @@ import java.util.*;
import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*; import static org.hamcrest.core.IsEqual.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@SuppressWarnings("JavaDoc") @SuppressWarnings("JavaDoc")
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@ -52,6 +54,8 @@ public class SQLiteHabitListTest extends BaseAndroidTest
private SQLiteRepository<HabitRecord> repository; private SQLiteRepository<HabitRecord> repository;
private ModelObservable.Listener listener;
@Override @Override
public void setUp() public void setUp()
{ {
@ -78,6 +82,16 @@ public class SQLiteHabitListTest extends BaseAndroidTest
} }
habitList.reload(); habitList.reload();
listener = mock(ModelObservable.Listener.class);
habitList.getObservable().addListener(listener);
}
@Override
protected void tearDown() throws Exception
{
habitList.getObservable().removeListener(listener);
super.tearDown();
} }
@Test @Test
@ -85,6 +99,8 @@ public class SQLiteHabitListTest extends BaseAndroidTest
{ {
Habit habit = modelFactory.buildHabit(); Habit habit = modelFactory.buildHabit();
habitList.add(habit); habitList.add(habit);
verify(listener).onModelChange();
exception.expect(IllegalArgumentException.class); exception.expect(IllegalArgumentException.class);
habitList.add(habit); habitList.add(habit);
} }

@ -92,6 +92,8 @@ public class SQLiteHabitList extends HabitList
record.copyFrom(habit); record.copyFrom(habit);
record.position = list.indexOf(habit); record.position = list.indexOf(habit);
repository.save(record); repository.save(record);
getObservable().notifyListeners();
} }
@Override @Override
@ -177,20 +179,21 @@ public class SQLiteHabitList extends HabitList
repository.remove(record); repository.remove(record);
}); });
rebuildOrder(); rebuildOrder();
getObservable().notifyListeners();
} }
@Override @Override
public synchronized void removeAll() public synchronized void removeAll()
{ {
loadRecords();
list.removeAll(); list.removeAll();
SQLiteDatabase db = DatabaseUtils.openDatabase(); SQLiteDatabase db = DatabaseUtils.openDatabase();
db.execSQL("delete from habits"); db.execSQL("delete from habits");
db.execSQL("delete from repetitions"); db.execSQL("delete from repetitions");
getObservable().notifyListeners();
} }
@Override @Override
public synchronized void reorder(Habit from, Habit to) public synchronized void reorder(@NonNull Habit from, @NonNull Habit to)
{ {
loadRecords(); loadRecords();
list.reorder(from, to); list.reorder(from, to);
@ -222,6 +225,7 @@ public class SQLiteHabitList extends HabitList
fromRecord.position = toPos; fromRecord.position = toPos;
repository.save(fromRecord); repository.save(fromRecord);
update(from); update(from);
getObservable().notifyListeners(); getObservable().notifyListeners();
} }
@ -251,6 +255,8 @@ public class SQLiteHabitList extends HabitList
record.copyFrom(h); record.copyFrom(h);
repository.save(record); repository.save(record);
} }
getObservable().notifyListeners();
} }
public void reload() public void reload()

@ -155,7 +155,7 @@ public abstract class HabitList implements Iterable<Habit>
* @param from the habit that should be moved * @param from the habit that should be moved
* @param to the habit that currently occupies the desired position * @param to the habit that currently occupies the desired position
*/ */
public abstract void reorder(Habit from, Habit to); public abstract void reorder(@NonNull Habit from, @NonNull Habit to);
public void repair() public void repair()
{ {

@ -36,31 +36,37 @@ import static org.isoron.uhabits.core.models.HabitList.Order.BY_SCORE;
public class MemoryHabitList extends HabitList public class MemoryHabitList extends HabitList
{ {
@NonNull @NonNull
private LinkedList<Habit> list; private LinkedList<Habit> list = new LinkedList<>();
private Comparator<Habit> comparator = null; private Comparator<Habit> comparator = null;
@NonNull @NonNull
private Order order; private Order order = Order.BY_POSITION;
@Nullable
private MemoryHabitList parent = null;
public MemoryHabitList() public MemoryHabitList()
{ {
super(); super();
list = new LinkedList<>();
order = Order.BY_POSITION;
} }
protected MemoryHabitList(@NonNull HabitMatcher matcher) protected MemoryHabitList(@NonNull HabitMatcher matcher,
Comparator<Habit> comparator,
@NonNull MemoryHabitList parent)
{ {
super(matcher); super(matcher);
list = new LinkedList<>(); this.parent = parent;
order = Order.BY_POSITION; this.comparator = comparator;
parent.getObservable().addListener(this::loadFromParent);
loadFromParent();
} }
@Override @Override
public synchronized void add(@NonNull Habit habit) public synchronized void add(@NonNull Habit habit)
throws IllegalArgumentException throws IllegalArgumentException
{ {
throwIfHasParent();
if (list.contains(habit)) if (list.contains(habit))
throw new IllegalArgumentException("habit already added"); throw new IllegalArgumentException("habit already added");
@ -71,6 +77,8 @@ public class MemoryHabitList extends HabitList
if (id == null) habit.setId((long) list.size()); if (id == null) habit.setId((long) list.size());
list.addLast(habit); list.addLast(habit);
resort(); resort();
getObservable().notifyListeners();
} }
@Override @Override
@ -78,7 +86,7 @@ public class MemoryHabitList extends HabitList
{ {
for (Habit h : list) for (Habit h : list)
{ {
if (h.getId() == null) continue; if (h.getId() == null) throw new IllegalStateException();
if (h.getId() == id) return h; if (h.getId() == id) return h;
} }
return null; return null;
@ -95,10 +103,7 @@ public class MemoryHabitList extends HabitList
@Override @Override
public synchronized HabitList getFiltered(HabitMatcher matcher) public synchronized HabitList getFiltered(HabitMatcher matcher)
{ {
MemoryHabitList habits = new MemoryHabitList(matcher); return new MemoryHabitList(matcher, comparator, this);
habits.comparator = comparator;
for (Habit h : this) if (matcher.matches(h)) habits.add(h);
return habits;
} }
@Override @Override
@ -115,12 +120,40 @@ public class MemoryHabitList extends HabitList
resort(); resort();
} }
private Comparator<Habit> getComparatorByOrder(Order order)
{
Comparator<Habit> nameComparator =
(h1, h2) -> h1.getName().compareTo(h2.getName());
Comparator<Habit> colorComparator = (h1, h2) ->
{
Integer c1 = h1.getColor();
Integer c2 = h2.getColor();
if (c1.equals(c2)) return nameComparator.compare(h1, h2);
else return c1.compareTo(c2);
};
Comparator<Habit> scoreComparator = (h1, h2) ->
{
double s1 = h1.getScores().getTodayValue();
double s2 = h2.getScores().getTodayValue();
return Double.compare(s2, s1);
};
if (order == BY_POSITION) return null;
if (order == BY_NAME) return nameComparator;
if (order == BY_COLOR) return colorComparator;
if (order == BY_SCORE) return scoreComparator;
throw new IllegalStateException();
}
@Override @Override
public int indexOf(@NonNull Habit h) public int indexOf(@NonNull Habit h)
{ {
return list.indexOf(h); return list.indexOf(h);
} }
@NonNull
@Override @Override
public Iterator<Habit> iterator() public Iterator<Habit> iterator()
{ {
@ -130,13 +163,23 @@ public class MemoryHabitList extends HabitList
@Override @Override
public synchronized void remove(@NonNull Habit habit) public synchronized void remove(@NonNull Habit habit)
{ {
throwIfHasParent();
list.remove(habit); list.remove(habit);
} }
@Override @Override
public synchronized void reorder(Habit from, Habit to) public synchronized void reorder(@NonNull Habit from, @NonNull Habit to)
{ {
throwIfHasParent();
if (indexOf(from) < 0)
throw new IllegalArgumentException(
"list does not contain (from) habit");
int toPos = indexOf(to); int toPos = indexOf(to);
if (toPos < 0)
throw new IllegalArgumentException(
"list does not contain (to) habit");
list.remove(from); list.remove(from);
list.add(toPos, from); list.add(toPos, from);
} }
@ -153,31 +196,20 @@ public class MemoryHabitList extends HabitList
// NOP // NOP
} }
private Comparator<Habit> getComparatorByOrder(Order order) private void throwIfHasParent()
{
Comparator<Habit> nameComparator =
(h1, h2) -> h1.getName().compareTo(h2.getName());
Comparator<Habit> colorComparator = (h1, h2) ->
{ {
Integer c1 = h1.getColor(); if (parent != null) throw new IllegalStateException(
Integer c2 = h2.getColor(); "Filtered lists cannot be modified directly. " +
if (c1.equals(c2)) return nameComparator.compare(h1, h2); "You should modify the parent list instead.");
else return c1.compareTo(c2); }
};
Comparator<Habit> scoreComparator = (h1, h2) -> private synchronized void loadFromParent()
{ {
double s1 = h1.getScores().getTodayValue(); if (parent == null) throw new IllegalStateException();
double s2 = h2.getScores().getTodayValue();
return Double.compare(s2, s1);
};
if (order == BY_POSITION) return null; list.clear();
if (order == BY_NAME) return nameComparator; for (Habit h : parent) if (filter.matches(h)) list.add(h);
if (order == BY_COLOR) return colorComparator; resort();
if (order == BY_SCORE) return scoreComparator;
throw new IllegalStateException();
} }
private synchronized void resort() private synchronized void resort()

@ -19,20 +19,29 @@
package org.isoron.uhabits.core.models; package org.isoron.uhabits.core.models;
import org.hamcrest.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.junit.*; import org.junit.*;
import org.junit.rules.*;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.fail;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.isoron.uhabits.core.models.HabitList.Order.*; import static org.hamcrest.MatcherAssert.*;
import static org.junit.Assert.*; import static org.isoron.uhabits.core.models.HabitList.Order.BY_COLOR;
import static org.isoron.uhabits.core.models.HabitList.Order.BY_NAME;
import static org.isoron.uhabits.core.models.HabitList.Order.BY_POSITION;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@SuppressWarnings("JavaDoc") @SuppressWarnings("JavaDoc")
public class HabitListTest extends BaseUnitTest public class HabitListTest extends BaseUnitTest
{ {
@Rule
public ExpectedException thrown = ExpectedException.none();
private ArrayList<Habit> habitsArray; private ArrayList<Habit> habitsArray;
private HabitList activeHabits; private HabitList activeHabits;
@ -69,45 +78,41 @@ public class HabitListTest extends BaseUnitTest
} }
@Test @Test
public void test_countActive() public void testSize()
{ {
assertThat(habitList.size(), equalTo(10));
assertThat(activeHabits.size(), equalTo(6)); assertThat(activeHabits.size(), equalTo(6));
assertThat(reminderHabits.size(), equalTo(4));
} }
@Test @Test
public void test_getByPosition() public void testGetByPosition()
{ {
assertThat(habitList.getByPosition(0), equalTo(habitsArray.get(0))); assertThat(habitList.getByPosition(0), equalTo(habitsArray.get(0)));
assertThat(habitList.getByPosition(3), equalTo(habitsArray.get(3))); assertThat(habitList.getByPosition(3), equalTo(habitsArray.get(3)));
assertThat(habitList.getByPosition(9), equalTo(habitsArray.get(9))); assertThat(habitList.getByPosition(9), equalTo(habitsArray.get(9)));
assertThat(activeHabits.getByPosition(0), equalTo(habitsArray.get(2))); assertThat(activeHabits.getByPosition(0), equalTo(habitsArray.get(2)));
}
@Test
public void test_getHabitsWithReminder()
{
assertThat(reminderHabits.size(), equalTo(4));
assertThat(reminderHabits.getByPosition(1), assertThat(reminderHabits.getByPosition(1),
equalTo(habitsArray.get(3))); equalTo(habitsArray.get(3)));
} }
@Test @Test
public void test_get_withInvalidId() public void testGetById()
{ {
assertThat(habitList.getById(100L), is(nullValue())); Habit habit1 = habitsArray.get(0);
Habit habit2 = habitList.getById(habit1.getId());
assertThat(habit1, equalTo(habit2));
} }
@Test @Test
public void test_get_withValidId() public void testGetById_withInvalidId()
{ {
Habit habit1 = habitsArray.get(0); assertNull(habitList.getById(100L));
Habit habit2 = habitList.getById(habit1.getId());
assertThat(habit1, equalTo(habit2));
} }
@Test @Test
public void test_ordering() public void testOrdering()
{ {
HabitList list = modelFactory.buildHabitList(); HabitList list = modelFactory.buildHabitList();
Habit h1 = fixtures.createEmptyHabit(); Habit h1 = fixtures.createEmptyHabit();
@ -155,7 +160,7 @@ public class HabitListTest extends BaseUnitTest
} }
@Test @Test
public void test_reorder() public void testReorder()
{ {
int operations[][] = { int operations[][] = {
{ 5, 2 }, { 3, 7 }, { 4, 4 }, { 3, 2 } { 5, 2 }, { 3, 7 }, { 4, 4 }, { 3, 2 }
@ -191,13 +196,16 @@ public class HabitListTest extends BaseUnitTest
} }
@Test @Test
public void test_size() public void testReorder_withInvalidArguments() throws Exception
{ {
assertThat(habitList.size(), equalTo(10)); Habit h1 = habitsArray.get(0);
Habit h2 = fixtures.createEmptyHabit();
thrown.expect(IllegalArgumentException.class);
habitList.reorder(h1, h2);
} }
@Test @Test
public void test_writeCSV() throws IOException public void testWriteCSV() throws IOException
{ {
HabitList list = modelFactory.buildHabitList(); HabitList list = modelFactory.buildHabitList();
@ -224,6 +232,43 @@ public class HabitListTest extends BaseUnitTest
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
list.writeCSV(writer); list.writeCSV(writer);
MatcherAssert.assertThat(writer.toString(), equalTo(expectedCSV)); assertThat(writer.toString(), equalTo(expectedCSV));
}
@Test
public void testAdd() throws Exception
{
Habit h1 = fixtures.createEmptyHabit();
assertFalse(h1.isArchived());
assertNull(h1.getId());
assertThat(habitList.indexOf(h1), equalTo(-1));
habitList.add(h1);
assertNotNull(h1.getId());
assertThat(habitList.indexOf(h1), not(equalTo(-1)));
assertThat(activeHabits.indexOf(h1), not(equalTo(-1)));
}
@Test
public void testAdd_withFilteredList() throws Exception
{
thrown.expect(IllegalStateException.class);
activeHabits.add(fixtures.createEmptyHabit());
}
@Test
public void testRemove_onFilteredList() throws Exception
{
thrown.expect(IllegalStateException.class);
activeHabits.remove(fixtures.createEmptyHabit());
}
@Test
public void testReorder_onFilteredList() throws Exception
{
Habit h1 = fixtures.createEmptyHabit();
Habit h2 = fixtures.createEmptyHabit();
thrown.expect(IllegalStateException.class);
activeHabits.reorder(h1, h2);
} }
} }
Loading…
Cancel
Save