Skip to content

Commit

Permalink
New constraints and defaults:
Browse files Browse the repository at this point in the history
- forbid DUE=START;
- use defaults satisfying DUE>START;
- shift DUE if START violates constraint;
- refactor: correct spelling of "constraint".
  • Loading branch information
korelstar committed Nov 22, 2016
1 parent 5ab95fb commit 06a53eb
Show file tree
Hide file tree
Showing 18 changed files with 343 additions and 34 deletions.
Expand Up @@ -30,10 +30,12 @@
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
import org.dmfs.tasks.model.adapters.TimezoneFieldAdapter;
import org.dmfs.tasks.model.adapters.UrlFieldAdapter;
import org.dmfs.tasks.model.contraints.AdjustPercentComplete;
import org.dmfs.tasks.model.contraints.ChecklistConstraint;
import org.dmfs.tasks.model.contraints.NotBefore;
import org.dmfs.tasks.model.contraints.ShiftIfAfter;
import org.dmfs.tasks.model.constraints.AdjustPercentComplete;
import org.dmfs.tasks.model.constraints.After;
import org.dmfs.tasks.model.constraints.BeforeOrShiftTime;
import org.dmfs.tasks.model.constraints.ChecklistConstraint;
import org.dmfs.tasks.model.defaults.DefaultAfter;
import org.dmfs.tasks.model.defaults.DefaultBefore;


/**
Expand All @@ -57,7 +59,7 @@ public final class TaskFieldAdapters
* Adapter for the status of a task.
*/
public final static IntegerFieldAdapter STATUS = (IntegerFieldAdapter) new IntegerFieldAdapter(Tasks.STATUS, Tasks.STATUS_NEEDS_ACTION)
.addContraint(new AdjustPercentComplete(PERCENT_COMPLETE));
.addConstraint(new AdjustPercentComplete(PERCENT_COMPLETE));

/**
* Adapter for the priority value of a task.
Expand Down Expand Up @@ -103,7 +105,7 @@ public final class TaskFieldAdapters
* Adapter for the checklist of a task.
*/
public final static ChecklistFieldAdapter CHECKLIST = (ChecklistFieldAdapter) new ChecklistFieldAdapter(Tasks.DESCRIPTION)
.addContraint(new ChecklistConstraint(STATUS, PERCENT_COMPLETE));
.addConstraint(new ChecklistConstraint(STATUS, PERCENT_COMPLETE));

/**
* Private adapter for the start date of a task. We need this to reference DTSTART from DUE.
Expand All @@ -113,13 +115,13 @@ public final class TaskFieldAdapters
/**
* Adapter for the due date of a task.
*/
public final static TimeFieldAdapter DUE = (TimeFieldAdapter) new TimeFieldAdapter(Tasks.DUE, Tasks.TZ, Tasks.IS_ALLDAY).addContraint(new NotBefore(
_DTSTART));
public final static TimeFieldAdapter DUE = (TimeFieldAdapter) new TimeFieldAdapter(Tasks.DUE, Tasks.TZ, Tasks.IS_ALLDAY)
.addConstraint(new After(_DTSTART)).setDefault(new DefaultAfter(_DTSTART));

/**
* Adapter for the start date of a task.
*/
public final static TimeFieldAdapter DTSTART = (TimeFieldAdapter) _DTSTART.addContraint(new ShiftIfAfter(DUE));
public final static TimeFieldAdapter DTSTART = (TimeFieldAdapter) _DTSTART.addConstraint(new BeforeOrShiftTime(DUE)).setDefault(new DefaultBefore(DUE));

/**
* Adapter for the completed date of a task.
Expand Down
6 changes: 3 additions & 3 deletions opentasks/src/main/java/org/dmfs/tasks/model/XmlModel.java
Expand Up @@ -26,7 +26,7 @@
import org.dmfs.tasks.model.adapters.BooleanFieldAdapter;
import org.dmfs.tasks.model.adapters.FieldAdapter;
import org.dmfs.tasks.model.adapters.StringFieldAdapter;
import org.dmfs.tasks.model.contraints.UpdateAllDay;
import org.dmfs.tasks.model.constraints.UpdateAllDay;
import org.dmfs.tasks.model.layout.LayoutDescriptor;
import org.dmfs.xmlobjects.ElementDescriptor;
import org.dmfs.xmlobjects.QualifiedName;
Expand Down Expand Up @@ -201,11 +201,11 @@ public XmlModel finish(ElementDescriptor<XmlModel> descriptor, XmlModel object,
// add UpdateAllDay constraint of due or start fields are missing to keep the values in sync with the allday flag
if (!state.hasDue)
{
((FieldAdapter<Boolean>) state.alldayDescriptor.getFieldAdapter()).addContraint(new UpdateAllDay(TaskFieldAdapters.DUE));
((FieldAdapter<Boolean>) state.alldayDescriptor.getFieldAdapter()).addConstraint(new UpdateAllDay(TaskFieldAdapters.DUE));
}
if (!state.hasStart)
{
((FieldAdapter<Boolean>) state.alldayDescriptor.getFieldAdapter()).addContraint(new UpdateAllDay(TaskFieldAdapters.DTSTART));
((FieldAdapter<Boolean>) state.alldayDescriptor.getFieldAdapter()).addConstraint(new UpdateAllDay(TaskFieldAdapters.DTSTART));
}
}
return object;
Expand Down
Expand Up @@ -22,7 +22,8 @@

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.OnContentChangeListener;
import org.dmfs.tasks.model.contraints.AbstractConstraint;
import org.dmfs.tasks.model.constraints.AbstractConstraint;
import org.dmfs.tasks.model.defaults.AbstractDefault;

import android.content.ContentValues;
import android.database.Cursor;
Expand All @@ -45,6 +46,12 @@ public abstract class FieldAdapter<Type>
private List<AbstractConstraint<Type>> mConstraints;


/**
* An optional custom default value which is used when a new value is set.
*/
private AbstractDefault<Type> mDefault;


/**
* Get the value from the given {@link ContentSet}
*
Expand All @@ -58,7 +65,7 @@ public abstract class FieldAdapter<Type>
/**
* Get the value from the given {@link Cursor}
*
* @param values
* @param cursor
* The {@link Cursor} that contain the value to return.
* @return The value.
*/
Expand All @@ -75,6 +82,31 @@ public abstract class FieldAdapter<Type>
*/
public abstract Type getDefault(ContentSet values);

/**
* Get a customizable default value for this Adapter.
* The algorithm used to generate the default value can be specified using {@link #setDefault(AbstractDefault)}.
*
* @param values The {@link ContentSet}.
* @return A default Value
*/
public Type getCustomizableDefault(ContentSet values) {
Type defaultValue = getDefault(values);
if(mDefault!=null) {
defaultValue = mDefault.getCustomDefault(values, defaultValue);
}
return defaultValue;
}

/**
* Set a custom default value which is used when a new value is set.
* @param customDefault Default for this Field
*/
public final FieldAdapter<Type> setDefault(AbstractDefault<Type> customDefault)
{
mDefault = customDefault;
return this;
}


/**
* Set a value in the given {@link ContentSet}.
Expand Down Expand Up @@ -139,16 +171,16 @@ public void validateAndSet(ContentSet values, Type value)
/**
* Add a new constraint to this field adapter. Constraints are evaluated in the order they have been added.
*
* @param contraint
* @param constraint
* The new constraint.
*/
public final FieldAdapter<Type> addContraint(AbstractConstraint<Type> contraint)
public final FieldAdapter<Type> addConstraint(AbstractConstraint<Type> constraint)
{
if (mConstraints == null)
{
mConstraints = new LinkedList<AbstractConstraint<Type>>();
}
mConstraints.add(contraint);
mConstraints.add(constraint);
return this;
}

Expand Down
Expand Up @@ -181,13 +181,7 @@ public Time getDefault(ContentSet values)
{
// make it an allday value
value.set(value.monthDay, value.month, value.year);
}
else
{
value.second = 0;
// round up to next quarter-hour
value.minute = ((value.minute + 14) / 15) * 15;
value.normalize(false);
value.timezone = Time.TIMEZONE_UTC; // all-day values are saved in UTC
}

return value;
Expand Down Expand Up @@ -291,4 +285,5 @@ public void unregisterListener(ContentSet values, OnContentChangeListener listen
values.removeOnChangeListener(listener, mAllDayField);
}
}

}
Expand Up @@ -15,7 +15,7 @@
*
*/

package org.dmfs.tasks.model.contraints;
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;

Expand Down
Expand Up @@ -15,7 +15,7 @@
*
*/

package org.dmfs.tasks.model.contraints;
package org.dmfs.tasks.model.constraints;

import org.dmfs.provider.tasks.TaskContract.Tasks;
import org.dmfs.tasks.model.ContentSet;
Expand Down
@@ -0,0 +1,53 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.dmfs.tasks.model.constraints;

import android.text.format.Time;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
import org.dmfs.tasks.model.defaults.AbstractDefault;
import org.dmfs.tasks.model.defaults.DefaultAfter;


/**
* Ensure a time is after a specific reference time. The new value will be set using {@link DefaultAfter} otherwise.
*/
public class After extends AbstractConstraint<Time>
{
private final TimeFieldAdapter mReferenceAdapter;
private final AbstractDefault<Time> mDefault;


public After(TimeFieldAdapter referenceAdapter)
{
mReferenceAdapter = referenceAdapter;
mDefault = new DefaultAfter(referenceAdapter);
}


@Override
public Time apply(ContentSet currentValues, Time oldValue, Time newValue)
{
Time reference = mReferenceAdapter.get(currentValues);
if (reference != null && newValue != null && !newValue.after(reference))
{
newValue.set(mDefault.getCustomDefault(currentValues, reference));
}
return newValue;
}

}
@@ -0,0 +1,79 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.dmfs.tasks.model.constraints;

import android.text.format.Time;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
import org.dmfs.tasks.model.defaults.AbstractDefault;
import org.dmfs.tasks.model.defaults.DefaultAfter;


/**
* Ensure a time is before a specific reference time.
* Otherwise, shift the reference time by the same amount that value has been shifted.
* If this still violated the constraint, then set the reference value to its default value.
*
* TODO: use Duration class to get the duration in days and shift without summer/winter time switches
*/
public class BeforeOrShiftTime extends AbstractConstraint<Time>
{
private final TimeFieldAdapter mReferenceAdapter;
private final AbstractDefault<Time> mDefault;


public BeforeOrShiftTime(TimeFieldAdapter referenceAdapter)
{
mReferenceAdapter = referenceAdapter;
mDefault = new DefaultAfter(null);
}


@Override
public Time apply(ContentSet currentValues, Time oldValue, Time newValue)
{
Time reference = mReferenceAdapter.get(currentValues);
if (reference != null && newValue != null)
{
if (oldValue != null && !newValue.before(reference))
{
// try to shift the reference value
long diff = newValue.toMillis(false) - oldValue.toMillis(false);
if (diff > 0)
{
boolean isAllDay = reference.allDay;
reference.set(reference.toMillis(false) + diff);

// ensure the event is still allday if is was allday before.
if (isAllDay)
{
reference.set(reference.monthDay, reference.month, reference.year);
}
mReferenceAdapter.set(currentValues, reference);
}
}
if (!newValue.before(reference))
{
// constraint is still violated, so set reference to its default value
reference.set(mDefault.getCustomDefault(currentValues, newValue));
mReferenceAdapter.set(currentValues, reference);
}
}
return newValue;
}

}
Expand Up @@ -15,7 +15,7 @@
*
*/

package org.dmfs.tasks.model.contraints;
package org.dmfs.tasks.model.constraints;

import java.util.List;

Expand Down
Expand Up @@ -15,7 +15,7 @@
*
*/

package org.dmfs.tasks.model.contraints;
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
Expand Down
Expand Up @@ -15,7 +15,7 @@
*
*/

package org.dmfs.tasks.model.contraints;
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
Expand Down
Expand Up @@ -15,7 +15,7 @@
*
*/

package org.dmfs.tasks.model.contraints;
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
Expand Down
Expand Up @@ -15,7 +15,7 @@
*
*/

package org.dmfs.tasks.model.contraints;
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
Expand Down
Expand Up @@ -15,7 +15,7 @@
*
*/

package org.dmfs.tasks.model.contraints;
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
Expand Down

0 comments on commit 06a53eb

Please sign in to comment.