Move models.sqlite to uhabits-core

This commit is contained in:
2017-06-20 12:50:57 -04:00
parent 96c1a046d4
commit b96385c4a7
35 changed files with 550 additions and 187 deletions

View File

@@ -9,6 +9,7 @@ dependencies {
apt 'com.google.auto.factory:auto-factory:1.0-beta3'
apt 'com.google.dagger:dagger:2.11-rc2'
compileOnly 'javax.annotation:jsr250-api:1.0'
compileOnly 'org.jetbrains:annotations-java5:15.0'
compileOnly 'com.google.auto.factory:auto-factory:1.0-beta3'
compileOnly 'com.google.dagger:dagger:2.11-rc2'
implementation 'com.android.support:support-annotations:25.3.1'

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.db;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface Column
{
String name() default "";
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.db;
public interface Cursor extends AutoCloseable
{
@Override
void close();
boolean moveToNext();
Integer getInt(int index);
Long getLong(int index);
Double getDouble(int index);
String getString(int index);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.db;
import java.util.*;
public interface Database
{
Cursor select(String query, String... params);
int update(String tableName,
Map<String, Object> values,
String where,
String... params);
Long insert(String tableName, Map<String, Object> values);
void delete(String tableName, String where, String... params);
void execSQL(String query, Object... params);
void beginTransaction();
void setTransactionSuccessful();
void endTransaction();
}

View File

@@ -0,0 +1,318 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.db;
import android.support.annotation.*;
import org.apache.commons.lang3.*;
import org.apache.commons.lang3.tuple.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
public class Repository<T>
{
@NonNull
private final Class klass;
@NonNull
private final Database db;
public Repository(@NonNull Class<T> klass, @NonNull Database db)
{
this.klass = klass;
this.db = db;
}
/**
* Returns the record that has the id provided.
* If no record is found, returns null.
*/
@Nullable
public T find(@NonNull Long id)
{
return findFirst(String.format("where %s=?", getIdName()),
id.toString());
}
/**
* Returns all records matching the given SQL query.
*
* The query should only contain the "where" part of the SQL query, and
* optinally the "order by" part. "Group by" is not allowed. If no matching
* records are found, returns an empty list.
*/
@NonNull
public List<T> findAll(@NonNull String query, @NonNull String... params)
{
try (Cursor c = db.select(buildSelectQuery() + query, params))
{
return cursorToMultipleRecords(c);
}
}
/**
* Returns the first record matching the given SQL query.
* See findAll for more details about the parameters.
*/
@Nullable
public T findFirst(String query, String... params)
{
try (Cursor c = db.select(buildSelectQuery() + query, params))
{
if (!c.moveToNext()) return null;
return cursorToSingleRecord(c);
}
}
/**
* Executes the given SQL query on the repository.
*
* The query can be of any kind. For example, complex deletes and updates
* are allowed. The repository does not perform any checks to guarantee
* that the query is valid, however the underlying database might.
*/
public void execSQL(String query, Object... params)
{
db.execSQL(query, params);
}
/**
* Executes the given callback inside a database transaction.
*
* If the callback terminates without throwing any exceptions, the
* transaction is considered successful. If any exceptions are thrown,
* the transaction is aborted. Nesting transactions is not allowed.
*/
public void executeAsTransaction(Runnable callback)
{
db.beginTransaction();
try
{
callback.run();
db.setTransactionSuccessful();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
finally
{
db.endTransaction();
}
}
/**
* Saves the record on the database.
*
* If the id of the given record is null, it is assumed that the record has
* not been inserted in the repository yet. The record will be inserted, a
* new id will be automatically generated, and the id of the given record
* will be updated.
*
* If the given record has a non-null id, then an update will be performed
* instead. That is, the previous record will be overwritten by the one
* provided.
*/
public void save(T record)
{
try
{
Field fields[] = getFields();
String columns[] = getColumnNames();
Map<String, Object> values = new HashMap<>();
for (int i = 0; i < fields.length; i++)
values.put(columns[i], fields[i].get(record));
Long id = (Long) getIdField().get(record);
int affectedRows = 0;
if (id != null) affectedRows =
db.update(getTableName(), values, getIdName() + "=?",
new String[]{ id.toString() });
if (id == null || affectedRows == 0)
{
id = db.insert(getTableName(), values);
getIdField().set(record, id);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
/**
* Removes the given record from the repository.
* The id of the given record is also set to null.
*/
public void remove(T record)
{
try
{
Long id = (Long) getIdField().get(record);
if (id == null) return;
db.delete(getTableName(), getIdName() + "=?",
new String[]{ id.toString() });
getIdField().set(record, null);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
@NonNull
private List<T> cursorToMultipleRecords(Cursor c)
{
List<T> records = new LinkedList<>();
while (c.moveToNext()) records.add(cursorToSingleRecord(c));
return records;
}
@NonNull
private T cursorToSingleRecord(Cursor cursor)
{
try
{
Constructor constructor = klass.getDeclaredConstructors()[0];
constructor.setAccessible(true);
T record = (T) constructor.newInstance();
int index = 0;
for (Field field : getFields())
copyFieldFromCursor(record, field, cursor, index++);
return record;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
private void copyFieldFromCursor(T record, Field field, Cursor c, int index)
throws IllegalAccessException
{
if (field.getType().isAssignableFrom(Integer.class))
field.set(record, c.getInt(index));
else if (field.getType().isAssignableFrom(Long.class))
field.set(record, c.getLong(index));
else if (field.getType().isAssignableFrom(Double.class))
field.set(record, c.getDouble(index));
else if (field.getType().isAssignableFrom(String.class))
field.set(record, c.getString(index));
else throw new RuntimeException(
"Type not supported: " + field.getType().getName() + " " +
field.getName());
}
private String buildSelectQuery()
{
return String.format("select %s from %s ",
StringUtils.join(getColumnNames(), ", "), getTableName());
}
private List<Pair<Field, Column>> getFieldColumnPairs()
{
List<Pair<Field, Column>> fields = new ArrayList<>();
for (Field field : klass.getDeclaredFields())
for (Annotation annotation : field.getAnnotations())
{
if (!(annotation instanceof Column)) continue;
Column column = (Column) annotation;
fields.add(new ImmutablePair<>(field, column));
}
return fields;
}
@NonNull
private Field[] getFields()
{
List<Field> fields = new ArrayList<>();
List<Pair<Field, Column>> columns = getFieldColumnPairs();
for (Pair<Field, Column> pair : columns) fields.add(pair.getLeft());
return fields.toArray(new Field[]{});
}
@NonNull
private String[] getColumnNames()
{
List<String> names = new ArrayList<>();
List<Pair<Field, Column>> columns = getFieldColumnPairs();
for (Pair<Field, Column> pair : columns)
{
String cname = pair.getRight().name();
if (cname.isEmpty()) cname = pair.getLeft().getName();
if (names.contains(cname))
throw new RuntimeException("duplicated column : " + cname);
names.add(cname);
}
return names.toArray(new String[]{});
}
@NonNull
private String getTableName()
{
String name = getTableAnnotation().name();
if (name.isEmpty()) throw new RuntimeException("Table name is empty");
return name;
}
@NonNull
private String getIdName()
{
String id = getTableAnnotation().id();
if (id.isEmpty()) throw new RuntimeException("Table id is empty");
return id;
}
@NonNull
private Field getIdField()
{
Field fields[] = getFields();
String idName = getIdName();
for (Field f : fields)
if (f.getName().equals(idName)) return f;
throw new RuntimeException("Field not found: " + idName);
}
@NonNull
private Table getTableAnnotation()
{
Table t = null;
for (Annotation annotation : klass.getAnnotations())
{
if (!(annotation instanceof Table)) continue;
t = (Table) annotation;
break;
}
if (t == null) throw new RuntimeException("Table annotation not found");
return t;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.db;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table
{
String name();
String id() default "id";
}

View File

@@ -19,6 +19,9 @@
package org.isoron.uhabits.core.models;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
/**
* Interface implemented by factories that provide concrete implementations of
* the core model classes.
@@ -44,4 +47,8 @@ public interface ModelFactory
ScoreList buildScoreList(Habit habit);
StreakList buildStreakList(Habit habit);
Repository<HabitRecord> buildHabitListRepository();
Repository<RepetitionRecord> buildRepetitionListRepository();
}

View File

@@ -19,28 +19,12 @@
package org.isoron.uhabits.core.models.memory;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import dagger.*;
@Module
public class MemoryModelFactory implements ModelFactory
{
@Provides
@AppScope
public static HabitList provideHabitList()
{
return new MemoryHabitList();
}
@Provides
@AppScope
public static ModelFactory provideModelFactory()
{
return new MemoryModelFactory();
}
@Override
public CheckmarkList buildCheckmarkList(Habit habit)
{
@@ -70,4 +54,16 @@ public class MemoryModelFactory implements ModelFactory
{
return new MemoryStreakList(habit);
}
@Override
public Repository<HabitRecord> buildHabitListRepository()
{
throw new IllegalStateException();
}
@Override
public Repository<RepetitionRecord> buildRepetitionListRepository()
{
throw new IllegalStateException();
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.models.sqlite;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import javax.inject.*;
/**
* Factory that provides models backed by an SQLite database.
*/
public class SQLModelFactory implements ModelFactory
{
private final Database db;
@Inject
public SQLModelFactory(Database db)
{
this.db = db;
}
@Override
public CheckmarkList buildCheckmarkList(Habit habit)
{
return new MemoryCheckmarkList(habit);
}
@Override
public HabitList buildHabitList()
{
return new SQLiteHabitList(this);
}
@Override
public RepetitionList buildRepetitionList(Habit habit)
{
return new SQLiteRepetitionList(habit, this);
}
@Override
public ScoreList buildScoreList(Habit habit)
{
return new MemoryScoreList(habit);
}
@Override
public StreakList buildStreakList(Habit habit)
{
return new MemoryStreakList(habit);
}
@Override
public Repository<HabitRecord> buildHabitListRepository()
{
return new Repository<>(HabitRecord.class, db);
}
@Override
public Repository<RepetitionRecord> buildRepetitionListRepository()
{
return new Repository<>(RepetitionRecord.class, db);
}
}

View File

@@ -0,0 +1,263 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.models.sqlite;
import android.support.annotation.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import java.util.*;
import javax.inject.*;
/**
* Implementation of a {@link HabitList} that is backed by SQLite.
*/
public class SQLiteHabitList extends HabitList
{
private static SQLiteHabitList instance;
@NonNull
private final Repository<HabitRecord> repository;
@NonNull
private final ModelFactory modelFactory;
@NonNull
private final MemoryHabitList list;
private boolean loaded = false;
@Inject
public SQLiteHabitList(@NonNull ModelFactory modelFactory)
{
super();
this.modelFactory = modelFactory;
this.list = new MemoryHabitList();
this.repository = modelFactory.buildHabitListRepository();
}
private void loadRecords()
{
if (loaded) return;
loaded = true;
list.removeAll();
List<HabitRecord> records = repository.findAll("order by position");
for (HabitRecord rec : records)
{
Habit h = modelFactory.buildHabit();
rec.copyTo(h);
list.add(h);
}
}
public static SQLiteHabitList getInstance(
@NonNull ModelFactory modelFactory)
{
if (instance == null) instance = new SQLiteHabitList(modelFactory);
return instance;
}
@Override
public synchronized void add(@NonNull Habit habit)
{
loadRecords();
list.add(habit);
HabitRecord record = new HabitRecord();
record.copyFrom(habit);
record.position = list.indexOf(habit);
repository.save(record);
getObservable().notifyListeners();
}
@Override
@Nullable
public Habit getById(long id)
{
loadRecords();
return list.getById(id);
}
@Override
@NonNull
public Habit getByPosition(int position)
{
loadRecords();
return list.getByPosition(position);
}
@NonNull
@Override
public HabitList getFiltered(HabitMatcher filter)
{
loadRecords();
return list.getFiltered(filter);
}
@Override
@NonNull
public Order getOrder()
{
return list.getOrder();
}
@Override
public void setOrder(@NonNull Order order)
{
list.setOrder(order);
}
@Override
public int indexOf(@NonNull Habit h)
{
loadRecords();
return list.indexOf(h);
}
@Override
public Iterator<Habit> iterator()
{
loadRecords();
return list.iterator();
}
private void rebuildOrder()
{
// List<Habit> habits = toList();
//
// int i = 0;
// for (Habit h : habits)
// {
// HabitRecord record = repository.find(h.getId());
// if (record == null)
// throw new RuntimeException("habit not in database");
//
// record.position = i++;
// repository.save(record);
// }
//
// update(habits);
}
@Override
public synchronized void remove(@NonNull Habit habit)
{
loadRecords();
list.remove(habit);
HabitRecord record = repository.find(habit.getId());
if (record == null) throw new RuntimeException("habit not in database");
repository.executeAsTransaction(() ->
{
((SQLiteRepetitionList) habit.getRepetitions()).removeAll();
repository.remove(record);
});
rebuildOrder();
getObservable().notifyListeners();
}
@Override
public synchronized void removeAll()
{
list.removeAll();
repository.execSQL("delete from habits");
repository.execSQL("delete from repetitions");
getObservable().notifyListeners();
}
@Override
public synchronized void reorder(@NonNull Habit from, @NonNull Habit to)
{
loadRecords();
list.reorder(from, to);
HabitRecord fromRecord = repository.find(from.getId());
HabitRecord toRecord = repository.find(to.getId());
if (fromRecord == null)
throw new RuntimeException("habit not in database");
if (toRecord == null)
throw new RuntimeException("habit not in database");
Integer fromPos = fromRecord.position;
Integer toPos = toRecord.position;
if (toPos < fromPos)
{
repository.execSQL("update habits set position = position + 1 " +
"where position >= ? and position < ?",
toPos, fromPos);
}
else
{
repository.execSQL("update habits set position = position - 1 " +
"where position > ? and position <= ?",
fromPos, toPos);
}
fromRecord.position = toPos;
repository.save(fromRecord);
update(from);
getObservable().notifyListeners();
}
@Override
public void repair()
{
loadRecords();
rebuildOrder();
}
@Override
public int size()
{
loadRecords();
return list.size();
}
@Override
public synchronized void update(List<Habit> habits)
{
loadRecords();
for (Habit h : habits)
{
HabitRecord record = repository.find(h.getId());
if (record == null)
throw new RuntimeException("habit not in database");
record.copyFrom(h);
repository.save(record);
}
getObservable().notifyListeners();
}
public void reload()
{
loaded = false;
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.models.sqlite;
import android.support.annotation.*;
import android.support.annotation.Nullable;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.jetbrains.annotations.*;
import java.util.*;
/**
* Implementation of a {@link RepetitionList} that is backed by SQLite.
*/
public class SQLiteRepetitionList extends RepetitionList
{
private final Repository<RepetitionRecord> repository;
private final MemoryRepetitionList list;
private boolean loaded = false;
public SQLiteRepetitionList(@NonNull Habit habit,
@NonNull ModelFactory modelFactory)
{
super(habit);
repository = modelFactory.buildRepetitionListRepository();
list = new MemoryRepetitionList(habit);
}
private void loadRecords()
{
if (loaded) return;
loaded = true;
check(habit.getId());
List<RepetitionRecord> records =
repository.findAll("where habit = ? order by timestamp",
habit.getId().toString());
for (RepetitionRecord rec : records)
list.add(rec.toRepetition());
}
@Override
public void add(Repetition rep)
{
loadRecords();
list.add(rep);
check(habit.getId());
RepetitionRecord record = new RepetitionRecord();
record.habit_id = habit.getId();
record.copyFrom(rep);
repository.save(record);
observable.notifyListeners();
}
@Override
public List<Repetition> getByInterval(long timeFrom, long timeTo)
{
loadRecords();
return list.getByInterval(timeFrom, timeTo);
}
@Override
@Nullable
public Repetition getByTimestamp(long timestamp)
{
loadRecords();
return list.getByTimestamp(timestamp);
}
@Override
public Repetition getOldest()
{
loadRecords();
return list.getOldest();
}
@Override
public Repetition getNewest()
{
loadRecords();
return list.getNewest();
}
@Override
public void remove(@NonNull Repetition repetition)
{
loadRecords();
list.remove(repetition);
check(habit.getId());
repository.execSQL(
"delete from repetitions where habit = ? and timestamp = ?",
habit.getId());
observable.notifyListeners();
}
public void removeAll()
{
loadRecords();
list.removeAll();
check(habit.getId());
repository.execSQL("delete from repetitions where habit = ?",
habit.getId());
}
@Override
public long getTotalCount()
{
loadRecords();
return list.getTotalCount();
}
public void reload()
{
loaded = false;
}
@Contract("null -> fail")
private void check(Long value)
{
if (value == null) throw new RuntimeException("null check failed");
}
}

View File

@@ -15,9 +15,11 @@
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core;
public class MyClass
{}
/**
* Provides SQLite implementations of the core models.
*/
package org.isoron.uhabits.core.models.sqlite;

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.models.sqlite.records;
import org.apache.commons.lang3.builder.*;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
/**
* The SQLite database record corresponding to a {@link Habit}.
*/
@Table(name = "habits")
public class HabitRecord
{
@Column
public String description;
@Column
public String name;
@Column(name = "freq_num")
public Integer freqNum;
@Column(name = "freq_den")
public Integer freqDen;
@Column
public Integer color;
@Column
public Integer position;
@Column(name = "reminder_hour")
public Integer reminderHour;
@Column(name = "reminder_min")
public Integer reminderMin;
@Column(name = "reminder_days")
public Integer reminderDays;
@Column
public Integer highlight;
@Column
public Integer archived;
@Column
public Integer type;
@Column(name = "target_value")
public Double targetValue;
@Column(name = "target_type")
public Integer targetType;
@Column
public String unit;
@Column
public Long id;
public void copyFrom(Habit model)
{
this.id = model.getId();
this.name = model.getName();
this.description = model.getDescription();
this.highlight = 0;
this.color = model.getColor();
this.archived = model.isArchived() ? 1 : 0;
this.type = model.getType();
this.targetType = model.getTargetType();
this.targetValue = model.getTargetValue();
this.unit = model.getUnit();
Frequency freq = model.getFrequency();
this.freqNum = freq.getNumerator();
this.freqDen = freq.getDenominator();
this.reminderDays = 0;
this.reminderMin = null;
this.reminderHour = null;
if (model.hasReminder())
{
Reminder reminder = model.getReminder();
this.reminderHour = reminder.getHour();
this.reminderMin = reminder.getMinute();
this.reminderDays = reminder.getDays().toInteger();
}
}
public void copyTo(Habit habit)
{
habit.setId(this.id);
habit.setName(this.name);
habit.setDescription(this.description);
habit.setFrequency(new Frequency(this.freqNum, this.freqDen));
habit.setColor(this.color);
habit.setArchived(this.archived != 0);
habit.setType(this.type);
habit.setTargetType(this.targetType);
habit.setTargetValue(this.targetValue);
habit.setUnit(this.unit);
if (reminderHour != null && reminderMin != null)
{
habit.setReminder(new Reminder(reminderHour, reminderMin,
new WeekdayList(reminderDays)));
}
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HabitRecord that = (HabitRecord) o;
return new EqualsBuilder()
.appendSuper(super.equals(o))
.append(freqNum, that.freqNum)
.append(freqDen, that.freqDen)
.append(color, that.color)
.append(position, that.position)
.append(reminderDays, that.reminderDays)
.append(highlight, that.highlight)
.append(archived, that.archived)
.append(type, that.type)
.append(targetValue, that.targetValue)
.append(targetType, that.targetType)
.append(name, that.name)
.append(description, that.description)
.append(reminderHour, that.reminderHour)
.append(reminderMin, that.reminderMin)
.append(unit, that.unit)
.isEquals();
}
@Override
public int hashCode()
{
return new HashCodeBuilder(17, 37)
.appendSuper(super.hashCode())
.append(name)
.append(description)
.append(freqNum)
.append(freqDen)
.append(color)
.append(position)
.append(reminderHour)
.append(reminderMin)
.append(reminderDays)
.append(highlight)
.append(archived)
.append(type)
.append(targetValue)
.append(targetType)
.append(unit)
.toHashCode();
}
@Override
public String toString()
{
return new ToStringBuilder(this)
.append("name", name)
.append("description", description)
.append("freqNum", freqNum)
.append("freqDen", freqDen)
.append("color", color)
.append("position", position)
.append("reminderHour", reminderHour)
.append("reminderMin", reminderMin)
.append("reminderDays", reminderDays)
.append("highlight", highlight)
.append("archived", archived)
.append("type", type)
.append("targetValue", targetValue)
.append("targetType", targetType)
.append("unit", unit)
.toString();
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.models.sqlite.records;
import org.isoron.uhabits.core.db.*;
import org.isoron.uhabits.core.models.*;
/**
* The SQLite database record corresponding to a {@link Repetition}.
*/
@Table(name = "Repetitions")
public class RepetitionRecord
{
public HabitRecord habit;
@Column(name = "habit")
public Long habit_id;
@Column
public Long timestamp;
@Column
public Integer value;
@Column
public Long id;
public void copyFrom(Repetition repetition)
{
timestamp = repetition.getTimestamp();
value = repetition.getValue();
}
public Repetition toRepetition()
{
return new Repetition(timestamp, value);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/**
* Provides classes that represent rows in the SQLite database.
*/
package org.isoron.uhabits.core.models.sqlite.records;

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.models.sqlite.records;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.junit.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
public class HabitRecordTest extends BaseUnitTest
{
private Habit habit;
@Before
@Override
public void setUp()
{
super.setUp();
habit = modelFactory.buildHabit();
habit.setName("Hello world");
habit.setDescription("Did you greet the world today?");
habit.setColor(1);
habit.setArchived(true);
habit.setFrequency(Frequency.THREE_TIMES_PER_WEEK);
habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
habit.setId(1000L);
}
@Test
public void testCopyFrom()
{
HabitRecord rec = new HabitRecord();
rec.copyFrom(habit);
assertThat(rec.name, equalTo(habit.getName()));
assertThat(rec.description, equalTo(habit.getDescription()));
assertThat(rec.color, equalTo(habit.getColor()));
assertThat(rec.archived, equalTo(1));
assertThat(rec.freqDen, equalTo(7));
assertThat(rec.freqNum, equalTo(3));
Reminder reminder = habit.getReminder();
assertThat(rec.reminderDays, equalTo(reminder.getDays().toInteger()));
assertThat(rec.reminderHour, equalTo(reminder.getHour()));
assertThat(rec.reminderMin, equalTo(reminder.getMinute()));
habit.setReminder(null);
rec.copyFrom(habit);
assertThat(rec.reminderMin, equalTo(null));
assertThat(rec.reminderHour, equalTo(null));
assertThat(rec.reminderDays, equalTo(0));
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.isoron.uhabits.core.models.sqlite.records;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.records.*;
import org.junit.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
public class RepetitionRecordTest extends BaseUnitTest
{
@Test
public void testRecord() throws Exception
{
Repetition rep = new Repetition(2000L, 50);
RepetitionRecord record = new RepetitionRecord();
record.copyFrom(rep);
assertThat(rep, equalTo(record.toRepetition()));
}
}