Convert HabitList and ModelObservable

pull/716/head
Quentin Hibon 5 years ago
parent 30630c3358
commit a58cbffb81

@ -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
// */
public abstract Order getPrimaryOrder(); // abstract fun setPrimaryOrder(order: Order)
public abstract Order getSecondaryOrder(); // /**
// * Changes the previous order of the elements on the list.
/** // *
* Changes the order of the elements on the list. // * @param order the new order criterion
* // */
* @param order the new order criterion // abstract fun setSecondaryOrder(order: Order)
*/
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()
} }
} }
Loading…
Cancel
Save