mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Convert HabitList and ModelObservable
This commit is contained in:
@@ -16,51 +16,45 @@
|
|||||||
* You should have received a copy of the GNU General Public License along
|
* You should have received a copy of the GNU General Public License along
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
package org.isoron.uhabits.core.models
|
||||||
|
|
||||||
package org.isoron.uhabits.core.models;
|
import com.opencsv.CSVWriter
|
||||||
|
import java.io.IOException
|
||||||
import androidx.annotation.*;
|
import java.io.Writer
|
||||||
|
import java.util.LinkedList
|
||||||
import com.opencsv.*;
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ordered collection of {@link Habit}s.
|
* An ordered collection of [Habit]s.
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
public abstract class HabitList implements Iterable<Habit>
|
abstract class HabitList : Iterable<Habit> {
|
||||||
{
|
val observable: ModelObservable
|
||||||
private final ModelObservable observable;
|
@JvmField
|
||||||
|
protected val filter: HabitMatcher
|
||||||
@NonNull
|
|
||||||
protected final HabitMatcher filter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new HabitList.
|
* Creates a new HabitList.
|
||||||
* <p>
|
*
|
||||||
|
*
|
||||||
* Depending on the implementation, this list can either be empty or be
|
* Depending on the implementation, this list can either be empty or be
|
||||||
* populated by some pre-existing habits, for example, from a certain
|
* populated by some pre-existing habits, for example, from a certain
|
||||||
* database.
|
* database.
|
||||||
*/
|
*/
|
||||||
public HabitList()
|
constructor() {
|
||||||
{
|
observable = ModelObservable()
|
||||||
observable = new ModelObservable();
|
filter = HabitMatcherBuilder().setArchivedAllowed(true).build()
|
||||||
filter = new HabitMatcherBuilder().setArchivedAllowed(true).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HabitList(@NonNull HabitMatcher filter)
|
protected constructor(filter: HabitMatcher) {
|
||||||
{
|
observable = ModelObservable()
|
||||||
observable = new ModelObservable();
|
this.filter = filter
|
||||||
this.filter = filter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a new habit in the list.
|
* Inserts a new habit in the list.
|
||||||
* <p>
|
*
|
||||||
|
*
|
||||||
* If the id of the habit is null, the list will assign it a new id, which
|
* If the id of the habit is null, the list will assign it a new id, which
|
||||||
* is guaranteed to be unique in the scope of the list. If id is not null,
|
* is guaranteed to be unique in the scope of the list. If id is not null,
|
||||||
* the caller should make sure that the list does not already contain
|
* the caller should make sure that the list does not already contain
|
||||||
@@ -69,8 +63,8 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
* @param habit the habit to be inserted
|
* @param habit the habit to be inserted
|
||||||
* @throws IllegalArgumentException if the habit is already on the list.
|
* @throws IllegalArgumentException if the habit is already on the list.
|
||||||
*/
|
*/
|
||||||
public abstract void add(@NonNull Habit habit)
|
@Throws(IllegalArgumentException::class)
|
||||||
throws IllegalArgumentException;
|
abstract fun add(habit: Habit)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the habit with specified id.
|
* Returns the habit with specified id.
|
||||||
@@ -78,8 +72,7 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
* @param id the id of the habit
|
* @param id the id of the habit
|
||||||
* @return the habit, or null if none exist
|
* @return the habit, or null if none exist
|
||||||
*/
|
*/
|
||||||
@Nullable
|
abstract fun getById(id: Long): Habit?
|
||||||
public abstract Habit getById(long id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the habit with specified UUID.
|
* Returns the habit with specified UUID.
|
||||||
@@ -87,8 +80,7 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
* @param uuid the UUID of the habit
|
* @param uuid the UUID of the habit
|
||||||
* @return the habit, or null if none exist
|
* @return the habit, or null if none exist
|
||||||
*/
|
*/
|
||||||
@Nullable
|
abstract fun getByUUID(uuid: String?): Habit?
|
||||||
public abstract Habit getByUUID(String uuid);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the habit that occupies a certain position.
|
* Returns the habit that occupies a certain position.
|
||||||
@@ -97,8 +89,7 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
* @return the habit at that position
|
* @return the habit at that position
|
||||||
* @throws IndexOutOfBoundsException when the position is invalid
|
* @throws IndexOutOfBoundsException when the position is invalid
|
||||||
*/
|
*/
|
||||||
@NonNull
|
abstract fun getByPosition(position: Int): Habit
|
||||||
public abstract Habit getByPosition(int position);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of habits that match a given condition.
|
* Returns the list of habits that match a given condition.
|
||||||
@@ -106,31 +97,23 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
* @param matcher the matcher that checks the condition
|
* @param matcher the matcher that checks the condition
|
||||||
* @return the list of matching habits
|
* @return the list of matching habits
|
||||||
*/
|
*/
|
||||||
@NonNull
|
abstract fun getFiltered(matcher: HabitMatcher?): HabitList
|
||||||
public abstract HabitList getFiltered(HabitMatcher matcher);
|
abstract var primaryOrder: Order
|
||||||
|
abstract var secondaryOrder: Order
|
||||||
|
|
||||||
public ModelObservable getObservable()
|
// /**
|
||||||
{
|
// * Changes the order of the elements on the list.
|
||||||
return observable;
|
// *
|
||||||
}
|
// * @param order the new order criterion
|
||||||
|
// */
|
||||||
|
// abstract fun setPrimaryOrder(order: Order)
|
||||||
|
|
||||||
public abstract Order getPrimaryOrder();
|
// /**
|
||||||
|
// * Changes the previous order of the elements on the list.
|
||||||
public abstract Order getSecondaryOrder();
|
// *
|
||||||
|
// * @param order the new order criterion
|
||||||
/**
|
// */
|
||||||
* Changes the order of the elements on the list.
|
// abstract fun setSecondaryOrder(order: Order)
|
||||||
*
|
|
||||||
* @param order the new order criterion
|
|
||||||
*/
|
|
||||||
public abstract void setPrimaryOrder(@NonNull Order order);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the previous order of the elements on the list.
|
|
||||||
*
|
|
||||||
* @param order the new order criterion
|
|
||||||
*/
|
|
||||||
public abstract void setSecondaryOrder(@NonNull Order order);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of the given habit in the list, or -1 if the list does
|
* Returns the index of the given habit in the list, or -1 if the list does
|
||||||
@@ -139,31 +122,28 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
* @param h the habit
|
* @param h the habit
|
||||||
* @return the index of the habit, or -1 if not in the list
|
* @return the index of the habit, or -1 if not in the list
|
||||||
*/
|
*/
|
||||||
public abstract int indexOf(@NonNull Habit h);
|
abstract fun indexOf(h: Habit): Int
|
||||||
|
val isEmpty: Boolean
|
||||||
public boolean isEmpty()
|
get() = size() == 0
|
||||||
{
|
|
||||||
return size() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the given habit from the list.
|
* Removes the given habit from the list.
|
||||||
* <p>
|
*
|
||||||
|
*
|
||||||
* If the given habit is not in the list, does nothing.
|
* If the given habit is not in the list, does nothing.
|
||||||
*
|
*
|
||||||
* @param h the habit to be removed.
|
* @param h the habit to be removed.
|
||||||
*/
|
*/
|
||||||
public abstract void remove(@NonNull Habit h);
|
abstract fun remove(h: Habit)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all the habits from the list.
|
* Removes all the habits from the list.
|
||||||
*/
|
*/
|
||||||
public void removeAll()
|
open fun removeAll() {
|
||||||
{
|
val copy: MutableList<Habit> = LinkedList()
|
||||||
List<Habit> copy = new LinkedList<>();
|
for (h in this) copy.add(h)
|
||||||
for (Habit h : this) copy.add(h);
|
for (h in copy) remove(h)
|
||||||
for (Habit h : copy) remove(h);
|
observable.notifyListeners()
|
||||||
observable.notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,40 +152,38 @@ 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(@NonNull Habit from, @NonNull Habit to);
|
abstract fun reorder(from: Habit, to: Habit)
|
||||||
|
open fun repair() {}
|
||||||
public void repair()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of habits in this list.
|
* Returns the number of habits in this list.
|
||||||
*
|
*
|
||||||
* @return number of habits
|
* @return number of habits
|
||||||
*/
|
*/
|
||||||
public abstract int size();
|
abstract fun size(): Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the list that a certain list of habits has been modified.
|
* Notifies the list that a certain list of habits has been modified.
|
||||||
* <p>
|
*
|
||||||
|
*
|
||||||
* Depending on the implementation, this operation might trigger a write to
|
* Depending on the implementation, this operation might trigger a write to
|
||||||
* disk, or do nothing at all. To make sure that the habits get persisted,
|
* disk, or do nothing at all. To make sure that the habits get persisted,
|
||||||
* this operation must be called.
|
* this operation must be called.
|
||||||
*
|
*
|
||||||
* @param habits the list of habits that have been modified.
|
* @param habits the list of habits that have been modified.
|
||||||
*/
|
*/
|
||||||
public abstract void update(List<Habit> habits);
|
abstract fun update(habits: List<Habit?>?)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the list that a certain habit has been modified.
|
* Notifies the list that a certain habit has been modified.
|
||||||
* <p>
|
*
|
||||||
* See {@link #update(List)} for more details.
|
*
|
||||||
|
* See [.update] for more details.
|
||||||
*
|
*
|
||||||
* @param habit the habit that has been modified.
|
* @param habit the habit that has been modified.
|
||||||
*/
|
*/
|
||||||
public void update(@NonNull Habit habit)
|
fun update(habit: Habit) {
|
||||||
{
|
update(listOf(habit))
|
||||||
update(Collections.singletonList(habit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,9 +195,9 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
* @param out the writer that will receive the result
|
* @param out the writer that will receive the result
|
||||||
* @throws IOException if write operations fail
|
* @throws IOException if write operations fail
|
||||||
*/
|
*/
|
||||||
public void writeCSV(@NonNull Writer out) throws IOException
|
@Throws(IOException::class)
|
||||||
{
|
fun writeCSV(out: Writer) {
|
||||||
String[] header = {
|
val header = arrayOf(
|
||||||
"Position",
|
"Position",
|
||||||
"Name",
|
"Name",
|
||||||
"Question",
|
"Question",
|
||||||
@@ -227,43 +205,27 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
"NumRepetitions",
|
"NumRepetitions",
|
||||||
"Interval",
|
"Interval",
|
||||||
"Color"
|
"Color"
|
||||||
};
|
)
|
||||||
|
val csv = CSVWriter(out)
|
||||||
CSVWriter csv = new CSVWriter(out);
|
csv.writeNext(header, false)
|
||||||
csv.writeNext(header, false);
|
for (habit in this) {
|
||||||
|
val (numerator, denominator) = habit.frequency
|
||||||
for (Habit habit : this)
|
val cols = arrayOf(
|
||||||
{
|
|
||||||
Frequency freq = habit.getFrequency();
|
|
||||||
|
|
||||||
String[] cols = {
|
|
||||||
String.format("%03d", indexOf(habit) + 1),
|
String.format("%03d", indexOf(habit) + 1),
|
||||||
habit.getName(),
|
habit.name,
|
||||||
habit.getQuestion(),
|
habit.question,
|
||||||
habit.getDescription(),
|
habit.description,
|
||||||
Integer.toString(freq.getNumerator()),
|
numerator.toString(),
|
||||||
Integer.toString(freq.getDenominator()),
|
denominator.toString(),
|
||||||
habit.getColor().toCsvColor(),
|
habit.color.toCsvColor()
|
||||||
};
|
)
|
||||||
|
csv.writeNext(cols, false)
|
||||||
csv.writeNext(cols, false);
|
|
||||||
}
|
}
|
||||||
|
csv.close()
|
||||||
csv.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void resort();
|
abstract fun resort()
|
||||||
|
enum class Order {
|
||||||
public enum Order
|
BY_NAME_ASC, BY_NAME_DESC, BY_COLOR_ASC, BY_COLOR_DESC, BY_SCORE_ASC, BY_SCORE_DESC, BY_STATUS_ASC, BY_STATUS_DESC, BY_POSITION
|
||||||
{
|
|
||||||
BY_NAME_ASC,
|
|
||||||
BY_NAME_DESC,
|
|
||||||
BY_COLOR_ASC,
|
|
||||||
BY_COLOR_DESC,
|
|
||||||
BY_SCORE_ASC,
|
|
||||||
BY_SCORE_DESC,
|
|
||||||
BY_STATUS_ASC,
|
|
||||||
BY_STATUS_DESC,
|
|
||||||
BY_POSITION
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,74 +16,70 @@
|
|||||||
* You should have received a copy of the GNU General Public License along
|
* You should have received a copy of the GNU General Public License along
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
package org.isoron.uhabits.core.models
|
||||||
|
|
||||||
package org.isoron.uhabits.core.models;
|
import java.util.LinkedList
|
||||||
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ModelObservable allows objects to subscribe themselves to it and receive
|
* A ModelObservable allows objects to subscribe themselves to it and receive
|
||||||
* notifications whenever the model is changed.
|
* notifications whenever the model is changed.
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
public class ModelObservable
|
class ModelObservable {
|
||||||
{
|
private val listeners: MutableList<Listener>
|
||||||
private List<Listener> listeners;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new ModelObservable with no listeners.
|
|
||||||
*/
|
|
||||||
public ModelObservable()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
listeners = new LinkedList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given listener to the observable.
|
* Adds the given listener to the observable.
|
||||||
*
|
*
|
||||||
* @param l the listener to be added.
|
* @param l the listener to be added.
|
||||||
*/
|
*/
|
||||||
public synchronized void addListener(Listener l)
|
@Synchronized
|
||||||
{
|
fun addListener(l: Listener) {
|
||||||
listeners.add(l);
|
listeners.add(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies every listener that the model has changed.
|
* Notifies every listener that the model has changed.
|
||||||
* <p>
|
*
|
||||||
|
*
|
||||||
* Only models should call this method.
|
* Only models should call this method.
|
||||||
*/
|
*/
|
||||||
public synchronized void notifyListeners()
|
@Synchronized
|
||||||
{
|
fun notifyListeners() {
|
||||||
for (Listener l : listeners) l.onModelChange();
|
for (l in listeners) l.onModelChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the given listener.
|
* Removes the given listener.
|
||||||
* <p>
|
*
|
||||||
|
*
|
||||||
* The listener will no longer be notified when the model changes. If the
|
* The listener will no longer be notified when the model changes. If the
|
||||||
* given listener is not subscribed to this observable, does nothing.
|
* given listener is not subscribed to this observable, does nothing.
|
||||||
*
|
*
|
||||||
* @param l the listener to be removed
|
* @param l the listener to be removed
|
||||||
*/
|
*/
|
||||||
public synchronized void removeListener(Listener l)
|
@Synchronized
|
||||||
{
|
fun removeListener(l: Listener) {
|
||||||
listeners.remove(l);
|
listeners.remove(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface implemented by objects that want to be notified when the model
|
* Interface implemented by objects that want to be notified when the model
|
||||||
* changes.
|
* changes.
|
||||||
*/
|
*/
|
||||||
public interface Listener
|
interface Listener {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Called whenever the model associated to this observable has been
|
* Called whenever the model associated to this observable has been
|
||||||
* modified.
|
* modified.
|
||||||
*/
|
*/
|
||||||
void onModelChange();
|
fun onModelChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ModelObservable with no listeners.
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
listeners = LinkedList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user