de.azapps.mirakel.model.task.TaskBase.java Source code

Java tutorial

Introduction

Here is the source code for de.azapps.mirakel.model.task.TaskBase.java

Source

/*******************************************************************************
 * Mirakel is an Android App for managing your ToDo-Lists
 *
 * Copyright (c) 2013-2014 Anatolij Zelenin, Georg Semmler.
 *
 *     This program 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
 *     any later version.
 *
 *     This program 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 de.azapps.mirakel.model.task;

import android.content.ContentValues;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.google.common.base.Optional;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import de.azapps.mirakel.DefinitionsHelper;
import de.azapps.mirakel.DefinitionsHelper.NoSuchListException;
import de.azapps.mirakel.DefinitionsHelper.SYNC_STATE;
import de.azapps.mirakel.helper.DateTimeHelper;
import de.azapps.mirakel.model.DatabaseHelper;
import de.azapps.mirakel.model.ModelBase;
import de.azapps.mirakel.model.list.ListMirakel;
import de.azapps.mirakel.model.list.SpecialList;
import de.azapps.mirakel.model.recurring.Recurring;
import de.azapps.mirakel.model.tags.Tag;
import de.azapps.tools.Log;

import static com.google.common.base.Optional.absent;
import static com.google.common.base.Optional.of;

abstract class TaskBase extends ModelBase {
    public static final String ADDITIONAL_ENTRIES = "additional_entries";
    public static final String CONTENT = "content";
    public static final String DONE = "done";
    public static final String DUE = "due";
    public static final String LIST_ID = "list_id";
    public static final String PRIORITY = "priority";
    public static final String PROGRESS = "progress";
    public static final String RECURRING = "recurring";
    public static final String RECURRING_REMINDER = "recurring_reminder";
    public static final String REMINDER = "reminder";
    public static final String RECURRING_SHOWN = "is_shown_recurring";
    private static final String TAG = "TaskBase";
    public static final String UUID = "uuid";

    private Map<String, String> additionalEntries = null;
    @NonNull
    protected String additionalEntriesString = "";
    @NonNull
    protected String content = "";
    @NonNull
    protected Calendar createdAt = new GregorianCalendar();
    protected boolean done;
    @NonNull
    protected Optional<Calendar> due = absent();
    @NonNull
    protected final Map<String, Boolean> edited = new HashMap<>();
    protected ListMirakel list;
    protected int priority;
    protected int progress;
    protected long recurrence;
    protected long recurringReminder;
    protected boolean isRecurringShown;
    @NonNull
    protected Optional<Calendar> reminder = absent();
    @NonNull
    protected SYNC_STATE syncState = SYNC_STATE.NOTHING;
    @NonNull
    protected Calendar updatedAt = new GregorianCalendar();
    @NonNull
    protected String uuid = "";
    @NonNull
    protected Optional<List<Tag>> tags = absent();
    private boolean isStub;

    TaskBase() {
        // nothing
        super(0L, "");
    }

    TaskBase(final long newId, @NonNull final String newUuid, @NonNull final ListMirakel newList,
            @NonNull final String newName, @NonNull final String newContent, final boolean newDone,
            final @NonNull Optional<Calendar> newDue, final @NonNull Optional<Calendar> newReminder,
            final int newPriority, @NonNull final Calendar newCreatedAt, @NonNull final Calendar newUpdatedAt,
            @NonNull final SYNC_STATE newSyncState, @NonNull final String newAdditionalEntriesString,
            final int recurring, final int newRecurringReminder, final int newProgress, final boolean shown) {
        super(newId, newName);
        this.uuid = newUuid;
        setList(newList, false);
        setContent(newContent);
        setDone(newDone);
        setDue(newDue);
        setReminder(newReminder);
        setPriority(newPriority);
        this.createdAt = newCreatedAt;
        this.updatedAt = newUpdatedAt;
        syncState = newSyncState;
        this.additionalEntriesString = newAdditionalEntriesString;
        this.recurrence = recurring;
        this.recurringReminder = newRecurringReminder;
        this.progress = newProgress;
        clearEdited();
        this.tags = absent();
        isRecurringShown = shown;
    }

    TaskBase(@NonNull final String newName, @NonNull final ListMirakel list) {
        super(0L, newName);
        this.uuid = java.util.UUID.randomUUID().toString();
        setList(list, false);
        setContent("");
        setDone(false);
        setDue(Optional.<Calendar>absent());
        setReminder(Optional.<Calendar>absent());
        this.priority = 0;
        this.createdAt = new GregorianCalendar();
        this.updatedAt = new GregorianCalendar();
        syncState = SYNC_STATE.NOTHING;
        this.recurrence = -1L;
        this.recurringReminder = -1L;
        this.progress = 0;
        isRecurringShown = true;
        clearEdited();
    }

    public void addAdditionalEntry(@NonNull final String key, final String value) {
        initAdditionalEntries();
        Log.d(TaskBase.TAG, "add: " + key + ':' + value);
        this.additionalEntries.put(key, value);
    }

    void clearEdited() {
        this.edited.clear();
    }

    /**
     * Use getAdditional[Type] instead
     *
     * @return
     */
    @Deprecated
    public Map<String, String> getAdditionalEntries() {
        initAdditionalEntries();
        return this.additionalEntries;
    }

    public boolean containsAdditional(final String key) {
        initAdditionalEntries();
        return additionalEntries.containsKey(key);
    }

    @Nullable
    public String getAdditionalRaw(final String key) {
        initAdditionalEntries();
        return this.additionalEntries.get(key);
    }

    @Nullable
    public String getAdditionalString(final String key) {
        final String str = getAdditionalRaw(key);
        if (str == null) {
            return null;
        }
        return str.substring(1, str.length() - 1);
    }

    @Nullable
    public Integer getAdditionalInt(final String key) {
        final String str = getAdditionalRaw(key);
        if (str == null) {
            return null;
        }
        return Integer.valueOf(str);
    }

    public boolean existAdditional(final String key) {
        initAdditionalEntries();
        return this.additionalEntries.containsKey(key);
    }

    void setIsRecurringShown(final boolean shown) {
        this.isRecurringShown = shown;
    }

    boolean isRecurringShown() {
        return this.isRecurringShown;
    }

    @NonNull
    protected String getAdditionalEntriesString() {
        initAdditionalEntries();
        this.additionalEntriesString = serializeAdditionalEntries(this.additionalEntries);
        return this.additionalEntriesString;
    }

    @NonNull
    public static String serializeAdditionalEntries(@NonNull final Map<String, String> additionalEntries) {
        final StringBuilder additionalEntriesStr = new StringBuilder("{");
        boolean first = true;
        for (final Entry<String, String> p : additionalEntries.entrySet()) {
            additionalEntriesStr.append(first ? "" : ",").append('"').append(p.getKey()).append("\":")
                    .append(p.getValue());
            first = false;
        }
        return additionalEntriesStr.toString() + '}';
    }

    @NonNull
    public String getContent() {
        return this.content;
    }

    @Override
    @NonNull
    public ContentValues getContentValues() throws NoSuchListException {
        final ContentValues cv = super.getContentValues();
        cv.put(TaskBase.UUID, this.uuid);
        cv.put(TaskBase.LIST_ID, this.list.getId());
        cv.put(TaskBase.CONTENT, this.content);
        cv.put(TaskBase.DONE, this.done);
        if (this.due.isPresent()) {
            if ((this.due.get().get(Calendar.HOUR) == 0) && (this.due.get().get(Calendar.MINUTE) == 0)
                    && (this.due.get().get(Calendar.SECOND) == 0)) {
                cv.put(TaskBase.DUE, this.due.get().getTimeInMillis() / 1000L);
            } else {
                cv.put(TaskBase.DUE, DateTimeHelper.getUTCTime(this.due));
            }
        } else {
            cv.put(TaskBase.DUE, (Integer) null);
        }
        cv.put(TaskBase.REMINDER, DateTimeHelper.getUTCTime(this.reminder));
        cv.put(TaskBase.PRIORITY, this.priority);
        cv.put(DatabaseHelper.CREATED_AT, this.createdAt.getTimeInMillis() / 1000L);
        cv.put(DatabaseHelper.UPDATED_AT, this.updatedAt.getTimeInMillis() / 1000L);
        cv.put(DatabaseHelper.SYNC_STATE_FIELD, this.syncState.toInt());
        cv.put(TaskBase.RECURRING, this.recurrence);
        cv.put(TaskBase.RECURRING_REMINDER, this.recurringReminder);
        cv.put(TaskBase.PROGRESS, this.progress);
        cv.put(TaskBase.RECURRING_SHOWN, this.isRecurringShown);
        cv.put("additional_entries", getAdditionalEntriesString());
        return cv;
    }

    @NonNull
    public Calendar getCreatedAt() {
        return this.createdAt;
    }

    @NonNull
    public Optional<Calendar> getDue() {
        return this.due;
    }

    @NonNull
    public ListMirakel getList() {
        if (this.list == null) {
            throw new RuntimeException("The task is not properly initialized. List is null!");
        }
        return this.list;
    }

    public int getPriority() {
        return this.priority;
    }

    public int getProgress() {
        return this.progress;
    }

    public long getRecurrenceId() {
        return this.recurrence;
    }

    @NonNull
    public Optional<Recurring> getRecurrence() {
        return Recurring.get(this.recurrence);
    }

    @NonNull
    public Optional<Recurring> getRecurringReminder() {
        return Recurring.get(this.recurringReminder);
    }

    public long getRecurringReminderId() {
        return this.recurringReminder;
    }

    @NonNull
    public Optional<Calendar> getReminder() {
        return this.reminder;
    }

    @NonNull
    public SYNC_STATE getSyncState() {
        return this.syncState;
    }

    @NonNull
    public Calendar getUpdatedAt() {
        return this.updatedAt;
    }

    @NonNull
    public String getUUID() {
        return this.uuid;
    }

    public boolean hasRecurringReminder() {
        return this.recurringReminder > 0;
    }

    /**
     * This function parses the additional fields only if it is necessary
     */
    private void initAdditionalEntries() {
        if (this.additionalEntries == null) {
            this.additionalEntries = parseAdditionalEntries(this.additionalEntriesString);
        }
    }

    protected void setAdditionalEntries(@NonNull final String additional) {
        this.additionalEntriesString = additional;
        initAdditionalEntries();
    }

    @NonNull
    public static Map<String, String> parseAdditionalEntries(@NonNull final String additionalEntriesString) {
        final Map<String, String> ret = new HashMap<>();
        if ((additionalEntriesString != null) && !additionalEntriesString.trim().equals("")
                && !additionalEntriesString.trim().equals("null")) {
            final String t = additionalEntriesString.substring(1, additionalEntriesString.length() - 1);// remove {
            // and }
            final String[] parts = t.split(",");
            String key = null;
            for (final String p : parts) {
                final String[] keyValue = p.split(":");
                if (keyValue.length == 2) {
                    key = keyValue[0];
                    key = key.replaceAll("\"", "");
                    ret.put(key, keyValue[1]);
                } else if ((keyValue.length == 1) && (key != null)) {
                    ret.put(key, ret.get(key) + ',' + keyValue[0]);
                }
            }
        }
        return ret;
    }

    public boolean isDone() {
        return this.done;
    }

    boolean isEdited() {
        return this.edited.size() > 0;
    }

    boolean isEdited(final String key) {
        return this.edited.containsKey(key);
    }

    public void setContent(@NonNull String newContent) {
        if (this.content.equals(newContent)) {
            return;
        }
        newContent = newContent.trim().replace("\\n", "\n");
        newContent = newContent.replace("\\\"", "\"");
        this.content = newContent.replace("\b", "");
        this.edited.put(TaskBase.CONTENT, true);
    }

    public void setCreatedAt(@NonNull final Calendar created_at) {
        this.createdAt = created_at;
    }

    /**
     * @param newDone is the task marked as done?
     * @return The new task if one was created
     */
    @NonNull
    public Optional<Task> setDone(final boolean newDone) {
        if (this.done == newDone) {
            return absent();
        }
        this.done = newDone;
        this.edited.put(TaskBase.DONE, true);
        if (newDone && (this.recurrence != -1) && this.due.isPresent()) {
            if (getRecurrence().isPresent()) {
                final Optional<Task> oldTask = Task.get(getId());
                if (!oldTask.isPresent()) {
                    return absent();
                }
                final Task newTask = getRecurrence().get().incrementRecurringDue(oldTask.get());
                // set the sync state of the old task to recurring, only show
                // the new one
                this.done = true;
                return of(newTask);
            }
            Log.wtf(TaskBase.TAG, "Recurring vanish");
        } else if (newDone) {
            this.setProgress(100);
        } else {
            this.setProgress(0);
        }
        return absent();
    }

    public void setDue(final @NonNull Optional<Calendar> newDue) {
        if (DateTimeHelper.equalsCalendar(this.due, newDue)) {
            return;
        }
        this.due = newDue;
        this.edited.put(TaskBase.DUE, true);
        if (!newDue.isPresent()) {
            setRecurrence(-1L);
        }
    }

    /**
     * Replaces the id of the current task by the foreign task. This is needed
     * if we want to override the current task by a remote task.
     *
     * @param t other task
     */
    public void takeIdFrom(@NonNull final Task t) {
        this.setId(t.getId());
    }

    public void setList(@NonNull final ListMirakel newList) {
        setList(newList, false);
    }

    public void setList(@NonNull final ListMirakel newList, final boolean removeNoListFlag) {
        if ((this.list != null) && (this.list.getId() == newList.getId())) {
            return;
        }
        if (newList.isSpecial()) {
            this.list = ((SpecialList) newList).getDefaultList();
        } else {
            this.list = newList;
        }
        this.edited.put(TaskBase.LIST_ID, true);
        if (removeNoListFlag) {
            if (this.additionalEntries == null) {
                initAdditionalEntries();
            }
            this.additionalEntries.remove(DefinitionsHelper.TW_NO_PROJECT);
        }
    }

    @Override
    public void setName(@NonNull final String newName) {
        if (getName().equals(newName)) {
            return;
        }
        super.setName(newName);
        // This can not happen (it's final!!  but we are in a constructor), but hey, that's java
        if (edited != null) {
            this.edited.put(ModelBase.NAME, true);
        }
    }

    public void setPriority(final int priority) {
        if (this.priority == priority) {
            return;
        }
        if ((priority > 2) || (priority < -2)) {
            throw new IllegalArgumentException("Priority is not in Range [-2,2]");
        }
        this.priority = priority;
        this.edited.put(TaskBase.PRIORITY, true);
    }

    public void setProgress(final int newProgress) {
        if (this.progress == newProgress) {
            return;
        }
        this.edited.put("progress", true);
        this.progress = newProgress;
    }

    public void setRecurrence(final long newRecurrence) {
        if (this.recurrence == newRecurrence) {
            return;
        }
        this.recurrence = newRecurrence;
        this.edited.put(TaskBase.RECURRING, true);
    }

    public void setRecurringReminder(final long newRecurrence) {
        if (this.recurringReminder == newRecurrence) {
            return;
        }
        this.recurringReminder = newRecurrence;
        this.edited.put(TaskBase.RECURRING_REMINDER, true);
    }

    public void setReminder(final @NonNull Optional<Calendar> newReminder) {
        setReminder(newReminder, false);
    }

    public void setReminder(final @NonNull Optional<Calendar> newReminder, final boolean force) {
        if (this.reminder.or(new GregorianCalendar()).equals(newReminder.or(new GregorianCalendar())) && !force) {
            return;
        }
        this.reminder = newReminder;
        this.edited.put(TaskBase.REMINDER, true);
        if (!newReminder.isPresent()) {
            setRecurringReminder(-1L);
        }
    }

    public void setSyncState(@NonNull final SYNC_STATE sync_state) {
        this.syncState = sync_state;
    }

    public void setUpdatedAt(@NonNull final Calendar updated_at) {
        this.updatedAt = updated_at;
    }

    public void setUUID(@NonNull final String newUuid) {
        this.uuid = newUuid;
    }

    @NonNull
    public Optional<Task> toggleDone() {
        return setDone(!this.done);
    }

    @NonNull
    public List<Tag> getTags() {
        checkTags();
        return this.tags.get();
    }

    private void checkTags() {
        if (!this.tags.isPresent()) {
            if (this.getId() == 0) {
                this.tags = of((List<Tag>) new ArrayList<Tag>(0));
            } else {
                this.tags = of(Tag.getTagsForTask(this.getId()));
            }
        }
    }

    protected void addTag(@NonNull final Tag tag) {
        checkTags();
        this.tags.get().add(tag);
    }

    protected void removeTag(@NonNull final Tag tag) {
        checkTags();
        this.tags.get().remove(tag);
    }

    public boolean isStub() {
        return isStub;
    }

    public void setStub(final boolean isStub) {
        this.isStub = isStub;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.additionalEntries == null) ? 0 : this.additionalEntries.hashCode());
        result = prime * result + (this.additionalEntriesString.hashCode());
        result = prime * result + this.content.hashCode();
        result = prime * result + (this.createdAt.hashCode());
        result = prime * result + (this.done ? 1231 : 1237);
        result = prime * result + (this.due.isPresent() ? this.due.get().hashCode() : 0);
        result = prime * result + (this.edited.hashCode());
        result = prime * result + (int) (this.getId() ^ (this.getId() >>> 32));
        result = prime * result + (this.isRecurringShown ? 1231 : 1237);
        result = prime * result + (this.list.hashCode());
        result = prime * result + (getName().hashCode());
        result = prime * result + this.priority;
        result = prime * result + this.progress;
        result = prime * result + (int) this.recurrence;
        result = prime * result + (int) this.recurringReminder;
        result = prime * result + (this.reminder.isPresent() ? this.reminder.get().hashCode() : 0);
        result = prime * result + (this.syncState.hashCode());
        result = prime * result + (!this.tags.isPresent() ? 0 : this.tags.get().hashCode());
        result = prime * result + (this.updatedAt.hashCode());
        result = prime * result + (this.uuid.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }
        if (((Object) this).getClass() != o.getClass()) {
            return false;
        }
        final TaskBase other = (TaskBase) o;
        if (this.additionalEntries == null) {
            if (other.additionalEntries != null) {
                return false;
            }
        } else if (!this.additionalEntries.equals(other.additionalEntries)) {
            return false;
        }
        if (!this.additionalEntriesString.equals(other.additionalEntriesString)) {
            return false;
        }
        if (!this.content.equals(other.content)) {
            return false;
        }
        // We should ignore the created_at date
        if (this.done != other.done) {
            return false;
        }
        if (!DateTimeHelper.equalsCalendar(this.due, other.due)) {
            return false;
        }
        if (this.getId() != other.getId()) {
            return false;
        }
        if (this.isRecurringShown != other.isRecurringShown) {
            return false;
        }
        if (!this.list.equals(other.list)) {
            return false;
        }
        if (!getName().equals(other.getName())) {
            return false;
        }
        if (this.priority != other.priority) {
            return false;
        }
        if (this.progress != other.progress) {
            return false;
        }
        if (getRecurrence().isPresent() && other.getRecurrence().isPresent()) {
            if (!this.getRecurrence().get().equals(other.getRecurrence().get())) {
                return false;
            }
        } else if (getRecurrence().isPresent() != other.getRecurrence().isPresent()) {
            return false;
        }

        if (getRecurringReminder().isPresent() && other.getRecurringReminder().isPresent()) {
            if (!this.getRecurringReminder().get().equals(other.getRecurringReminder().get())) {
                return false;
            }
        } else if (getRecurringReminder().isPresent() != other.getRecurringReminder().isPresent()) {
            return false;
        }

        if (!DateTimeHelper.equalsCalendar(this.reminder, other.reminder)) {
            return false;
        }
        if (this.syncState != other.syncState) {
            return false;
        }
        if (!this.getTags().equals(other.getTags())) {
            return false;
        }
        // Do not compare updatedAt because it is updated
        if (!this.uuid.equals(other.uuid)) {
            return false;
        }
        return true;
    }

}