diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..68cc094d8 Binary files /dev/null and b/.DS_Store differ diff --git a/src/main/java/li/strolch/model/timedstate/ITimedState.java b/src/main/java/li/strolch/model/timedstate/ITimedState.java new file mode 100644 index 000000000..a5d0a2744 --- /dev/null +++ b/src/main/java/li/strolch/model/timedstate/ITimedState.java @@ -0,0 +1,45 @@ +package li.strolch.model.timedstate; + +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.ITimeVariable; +import li.strolch.model.timevalue.IValue; +import li.strolch.model.timevalue.IValueChange; + +/** + * A time based state characterized by a {@link IValue} object implementation. + * + * @author martin_smock + * + * @param + * IValue implementation representing the state at a given time + */ +@SuppressWarnings("rawtypes") +public interface ITimedState { + + /** + * @return the new {@link ITimeValue} matching the value in the future + */ + ITimeValue getNextMatch(final Long time, T value); + + /** + * @return the new {@link ITimeValue} matching the value in the future + */ + ITimeValue getPreviousMatch(final Long time, T value); + + /** + * @param change + * the state change to be applied + */ + void applyChange(final IValueChange change); + + /** + * @return the state at the given time + */ + ITimeValue getStateAt(final Long time); + + /** + * @return get the states evolution in time + */ + ITimeVariable getTimeEvolution(); + +} \ No newline at end of file diff --git a/src/main/java/li/strolch/model/timedstate/TimedState.java b/src/main/java/li/strolch/model/timedstate/TimedState.java new file mode 100644 index 000000000..f5dcfd089 --- /dev/null +++ b/src/main/java/li/strolch/model/timedstate/TimedState.java @@ -0,0 +1,64 @@ +package li.strolch.model.timedstate; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.ITimeVariable; +import li.strolch.model.timevalue.IValue; +import li.strolch.model.timevalue.IValueChange; +import li.strolch.model.timevalue.impl.TimeVariable; + + +/** + * @author martin_smock + */ +@SuppressWarnings("rawtypes") +public class TimedState implements ITimedState { + + private ITimeVariable timeVariable = new TimeVariable(); + + @Override + @SuppressWarnings("unchecked") + public ITimeValue getNextMatch(final Long time, final T value) { + Collection> futureValues = timeVariable.getFutureValues(time); + for (ITimeValue iTimeValue : futureValues) { + if (iTimeValue.getValue().matches(value)) { + return iTimeValue; + } + } + return null; + } + + @Override + @SuppressWarnings("unchecked") + public ITimeValue getPreviousMatch(final Long time, final T value) { + Collection> pastValues = timeVariable.getPastValues(time); + List> asList = new ArrayList>(pastValues); + Collections.reverse(asList); + for (ITimeValue iTimeValue : asList) { + if (iTimeValue.getValue().matches(value)) { + return iTimeValue; + } + } + return null; + } + + @Override + public void applyChange(final IValueChange change) { + timeVariable.applyChange(change); + } + + @Override + public ITimeValue getStateAt(final Long time) { + return timeVariable.getValueAt(time); + } + + @Override + public ITimeVariable getTimeEvolution() { + return timeVariable; + } + +} diff --git a/src/main/java/li/strolch/model/timevalue/ITimeValue.java b/src/main/java/li/strolch/model/timevalue/ITimeValue.java new file mode 100644 index 000000000..1df712d26 --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/ITimeValue.java @@ -0,0 +1,24 @@ +package li.strolch.model.timevalue; + +import li.strolch.model.timevalue.impl.TimeVariable; + +/** + * Interface for timed value objects to be used with the {@link TimeVariable} + * + * @author martin_smock + * + * @param + * the backing value of the timed value object + */ +@SuppressWarnings("rawtypes") +public interface ITimeValue extends Comparable> { + + ITimeValue setValue(final T value); + + T getValue(); + + Long getTime(); + + ITimeValue add(final T change); + +} diff --git a/src/main/java/li/strolch/model/timevalue/ITimeVariable.java b/src/main/java/li/strolch/model/timevalue/ITimeVariable.java new file mode 100644 index 000000000..4df20ff26 --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/ITimeVariable.java @@ -0,0 +1,70 @@ +package li.strolch.model.timevalue; + +import java.util.Collection; + +/** + * A timed variable storing a ordered sequence of {@link ITimeValue} objects + * modeling a time evolution of a quantity. + * + * @author martin_smock + * + * @param + * the backing value of the timed value object + */ +@SuppressWarnings("rawtypes") +public interface ITimeVariable { + + /** + * set the value at a point in time to a given time value object + * + * @param time + * the time to set the {@link IValue} + * @param value + * the {@link IValue} to set + */ + void setValueAt(final Long time, final T value); + + /** + * get the latest {@link ITimeValue} whose time field is less or equal to + * the time given + */ + ITimeValue getValueAt(final Long time); + + /** + * Applies a {@link IValueChange} propagating the change to all future + * values starting from the time of the change. + * + * @param change + * the {@link IValueChange} to be applied + */ + void applyChange(final IValueChange change); + + /** + * Get all {@link ITimeValue} objects whose time field is greater or equal + * to the given time + * + * @param time + * the time the sequence starts with + * @return the sequence of {@link ITimeValue} objects in the future + */ + Collection> getFutureValues(final Long time); + + /** + * Get all {@link ITimeValue} objects whose time field is strictly smaller + * than the given time + * + * @param time + * the time the sequence starts with + * @return the sequence of {@link ITimeValue} objects in the future + */ + Collection> getPastValues(final Long time); + + /** + * removes {@link ITimeValue} objects from the sequence, where the successor + * matches value. I.e considering a pair of adjacent {@link ITimeValue} + * objects in the sequence which have the same {@link IValue}, the later one + * is removed, since it contains no additional information. + */ + void compact(); + +} diff --git a/src/main/java/li/strolch/model/timevalue/IValue.java b/src/main/java/li/strolch/model/timevalue/IValue.java new file mode 100644 index 000000000..e3e4444ab --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/IValue.java @@ -0,0 +1,41 @@ +package li.strolch.model.timevalue; + +/** + * A value object defining some basic algebraic operations. Mathematically + * speaking {@link IValue} objects define a group with a addition operation. + * + * @author martin_smock + * + * @param + * any object for which a (generalized) add operation can be defined. + * + */ +public interface IValue { + + /** + * @return the backing value + */ + T getValue(); + + /** + * @return a value with the backing value added to the argument value + */ + IValue add(T o); + + /** + * @return true, if the backing values match. + */ + boolean matches(IValue other); + + /** + * @return the inverse value, such that add(value.getInverse()) returns the + * neutral element of the group + */ + IValue getInverse(); + + /** + * @return a copy of this + */ + IValue getCopy(); + +} diff --git a/src/main/java/li/strolch/model/timevalue/IValueChange.java b/src/main/java/li/strolch/model/timevalue/IValueChange.java new file mode 100644 index 000000000..ec25d5b18 --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/IValueChange.java @@ -0,0 +1,28 @@ +package li.strolch.model.timevalue; + +/** + * Interface for operators to be used to change the values of {@link ITimeValue} + * in a {@link ITimeVariable}. + * + * @author martin_smock + */ +@SuppressWarnings("rawtypes") +public interface IValueChange { + + /** + * @return the time this change has to be applied + */ + Long getTime(); + + /** + * @return the value of the change + */ + T getValue(); + + /** + * @return the inverse neutralizing a change. Very useful to undo changes + * applied. + */ + IValueChange getInverse(); + +} diff --git a/src/main/java/li/strolch/model/timevalue/impl/AString.java b/src/main/java/li/strolch/model/timevalue/impl/AString.java new file mode 100644 index 000000000..cd952facc --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/impl/AString.java @@ -0,0 +1,69 @@ +package li.strolch.model.timevalue.impl; + +/** + * Wrapper for java.util.String object defining a inverse to support algebraic + * operations. + * + * @author martin_smock + */ +public class AString { + + private final String string; + private final boolean inverse; + + public AString(final String string) { + this.string = string; + this.inverse = false; + } + + public AString(final String string, final boolean inverse) { + this.string = string; + this.inverse = inverse; + } + + public String getString() { + return string; + } + + public boolean isInverse() { + return inverse; + } + + public AString getInverse() { + return new AString(string, !inverse); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (inverse ? 1231 : 1237); + result = prime * result + ((string == null) ? 0 : string.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AString other = (AString) obj; + if (inverse != other.inverse) + return false; + if (string == null) { + if (other.string != null) + return false; + } else if (!string.equals(other.string)) + return false; + return true; + } + + @Override + public String toString() { + return "AString [string=" + string + ", inverse=" + inverse + "]"; + } + +} diff --git a/src/main/java/li/strolch/model/timevalue/impl/DoubleValue.java b/src/main/java/li/strolch/model/timevalue/impl/DoubleValue.java new file mode 100644 index 000000000..f4a73fdff --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/impl/DoubleValue.java @@ -0,0 +1,94 @@ +package li.strolch.model.timevalue.impl; + +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.IValue; + +/** + * {@link IValue} implementation to work with Double valued {@link ITimeValue} objects + * + * @author martin_smock + */ +public class DoubleValue implements IValue { + + public static final DoubleValue NEUTRAL = new DoubleValue(0.0d); + + private Double value; + + public DoubleValue(Double value) { + this.value = value; + } + + public DoubleValue(double value) { + this.value = Double.valueOf(value); + } + + public DoubleValue(Integer value) { + this.value = this.value.doubleValue(); + } + + public DoubleValue(int value) { + this.value = Integer.valueOf(value).doubleValue(); + } + + public DoubleValue(String valueAsString) throws NumberFormatException { + this.value = Double.parseDouble(valueAsString); + } + + @Override + public DoubleValue add(Double o) { + value += o; + return this; + } + + @Override + public Double getValue() { + return value; + } + + @Override + public String toString() { + return "DoubleValue [value=" + value + "]"; + } + + @Override + public boolean matches(IValue other) { + return this.value.equals(other.getValue()); + } + + @Override + public DoubleValue getInverse() { + return new DoubleValue(-getValue()); + } + + @Override + public DoubleValue getCopy(){ + return new DoubleValue(value); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DoubleValue other = (DoubleValue) obj; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + +} diff --git a/src/main/java/li/strolch/model/timevalue/impl/IntegerValue.java b/src/main/java/li/strolch/model/timevalue/impl/IntegerValue.java new file mode 100644 index 000000000..6b1964318 --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/impl/IntegerValue.java @@ -0,0 +1,86 @@ +package li.strolch.model.timevalue.impl; + +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.IValue; + +/** + * {@link IValue} implementation to work with Integer valued {@link ITimeValue} + * objects + * + * @author martin_smock + */ +public class IntegerValue implements IValue { + + public static final IntegerValue NEUTRAL = new IntegerValue(0); + + private Integer value; + + public IntegerValue(Integer value) { + this.value = value; + } + + public IntegerValue(int value) { + this.value = Integer.valueOf(value); + } + + public IntegerValue(String valueAsString) throws NumberFormatException { + this.value = Integer.parseInt(valueAsString); + } + + @Override + public IntegerValue add(Integer o) { + value += o; + return this; + } + + @Override + public boolean matches(IValue other) { + return this.value.equals(other.getValue()); + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public IntegerValue getInverse() { + return new IntegerValue(-getValue()); + } + + @Override + public String toString() { + return "IntegerValue [value=" + value + "]"; + } + + @Override + public IntegerValue getCopy() { + return new IntegerValue(value); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IntegerValue other = (IntegerValue) obj; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + +} diff --git a/src/main/java/li/strolch/model/timevalue/impl/StringSetValue.java b/src/main/java/li/strolch/model/timevalue/impl/StringSetValue.java new file mode 100644 index 000000000..499211bfc --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/impl/StringSetValue.java @@ -0,0 +1,86 @@ +package li.strolch.model.timevalue.impl; + +import java.util.HashSet; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.IValue; + + +/** + * {@link IValue} implementation to work with String valued {@link ITimeValue} + * objects. Since a java.util.String object does not define a inverse, a + * algebraic {@link AString} wrapper is used. + * + * @author martin_smock + */ +public class StringSetValue implements IValue> { + + private static Set neu = Collections.emptySet(); + public static final IValue> NEUTRAL = new StringSetValue(neu); + + private Set aStrings = new HashSet(); + + public StringSetValue() { + } + + public StringSetValue(final Set aStrings) { + this.aStrings = aStrings; + } + + @Override + public Set getValue() { + return aStrings; + } + + @Override + public IValue> add(Set o) { + + Set toBeAdded = new HashSet(o); + + for (Iterator iter1 = toBeAdded.iterator(); iter1.hasNext();) { + AString toAdd = iter1.next(); + for (Iterator iter = aStrings.iterator(); iter.hasNext();) { + AString aString = iter.next(); + boolean valueMatch = aString.getString().equals(toAdd.getString()); + boolean compensate = (toAdd.isInverse() && !aString.isInverse()) + || (!toAdd.isInverse() && aString.isInverse()); + if (valueMatch && compensate) { + iter.remove(); + iter1.remove(); + } + } + } + aStrings.addAll(toBeAdded); + return this; + } + + @Override + public boolean matches(IValue> other) { + return this.getValue().equals(other.getValue()); + } + + @Override + public IValue> getInverse() { + Set inverseSet = new HashSet(); + for (AString as : aStrings) { + inverseSet.add(as.getInverse()); + } + StringSetValue inverse = new StringSetValue(); + inverse.aStrings = inverseSet; + return inverse; + } + + @Override + public StringSetValue getCopy() { + return new StringSetValue(aStrings); + } + + @Override + public String toString() { + return "StringSetValue [aStrings=" + aStrings + "]"; + } + +} diff --git a/src/main/java/li/strolch/model/timevalue/impl/TimeValue.java b/src/main/java/li/strolch/model/timevalue/impl/TimeValue.java new file mode 100644 index 000000000..bd14fed55 --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/impl/TimeValue.java @@ -0,0 +1,90 @@ +package li.strolch.model.timevalue.impl; + +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.IValue; + +/** + * @author martin_smock + */ +@SuppressWarnings("rawtypes") +public class TimeValue implements ITimeValue { + + protected final Long time; + protected T value; + + /** + * @param time + * @param value + */ + public TimeValue(final Long time, final T value){ + this.time = time; + this.value = value; + } + + @Override + @SuppressWarnings("unchecked") + public T getValue() { + return (T) value.getCopy(); + } + + @Override + public Long getTime() { + return time; + } + + @Override + public ITimeValue setValue(final T value) { + this.value = value; + return this; + } + + @SuppressWarnings("unchecked") + @Override + public ITimeValue add(final T change) { + this.value.add(change.getValue()); + return this; + } + + @Override + public int compareTo(final ITimeValue arg0) { + return this.getTime().compareTo(arg0.getTime()); + } + + @Override + public String toString() { + return "TimeValue [time=" + time + ", value=" + value + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((time == null) ? 0 : time.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + @SuppressWarnings("unchecked") + TimeValue other = (TimeValue) obj; + if (time == null) { + if (other.time != null) + return false; + } else if (!time.equals(other.time)) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + +} diff --git a/src/main/java/li/strolch/model/timevalue/impl/TimeVariable.java b/src/main/java/li/strolch/model/timevalue/impl/TimeVariable.java new file mode 100644 index 000000000..43a71c63f --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/impl/TimeVariable.java @@ -0,0 +1,98 @@ +package li.strolch.model.timevalue.impl; + +import java.util.Collection; +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; + +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.ITimeVariable; +import li.strolch.model.timevalue.IValue; +import li.strolch.model.timevalue.IValueChange; + + +/** + * @author martin_smock + */ +@SuppressWarnings("rawtypes") +public class TimeVariable implements ITimeVariable { + + public SortedSet> container = new TreeSet>(); + + @Override + public ITimeValue getValueAt(final Long time) { + ITimeValue tmp = null; + for (ITimeValue value : container) { + if (value.getTime() <= time) { + tmp = value; + } else { + break; + } + } + return tmp; + } + + @Override + public void setValueAt(final Long time, final T targetValue) { + ITimeValue current = getValueAt(time); + if (current != null && current.getTime().equals(time)) { + current.setValue(targetValue); + } else { + container.add(new TimeValue(time, targetValue)); + } + } + + @Override + public SortedSet> getFutureValues(final Long time) { + TimeValue picker = new TimeValue(time, null); + return new TreeSet>(container.tailSet(picker)); + } + + @Override + public Collection> getPastValues(final Long time) { + TimeValue picker = new TimeValue(time, null); + return new TreeSet>(container.headSet(picker)); + } + + @Override + public void applyChange(final IValueChange change) { + + SortedSet> futureValues = getFutureValues(change.getTime()); + for (ITimeValue value : futureValues) { + value.add(change.getValue()); + } + + ITimeValue initialValue = getValueAt(change.getTime()); + if (initialValue == null) { + ITimeValue newValue = new TimeValue(change.getTime(), change.getValue()); + container.add(newValue); + } else if (initialValue.getTime().longValue() < change.getTime().longValue()) { + ITimeValue newValue = new TimeValue(change.getTime(), initialValue.getValue()); + newValue.add(change.getValue()); + container.add(newValue); + } + compact(); + } + + @SuppressWarnings("unchecked") + @Override + public void compact() { + + if (container.size() < 2) + return; + + Iterator> iterator = container.iterator(); + ITimeValue predecessor = iterator.next(); + + while (iterator.hasNext()) { + ITimeValue successor = iterator.next(); + if (successor.getValue().matches(predecessor.getValue())) { + iterator.remove(); + } else { + predecessor = successor; + } + } + + } + +} diff --git a/src/main/java/li/strolch/model/timevalue/impl/ValueChange.java b/src/main/java/li/strolch/model/timevalue/impl/ValueChange.java new file mode 100644 index 000000000..8d7d33664 --- /dev/null +++ b/src/main/java/li/strolch/model/timevalue/impl/ValueChange.java @@ -0,0 +1,77 @@ +package li.strolch.model.timevalue.impl; + +import li.strolch.model.timevalue.IValue; +import li.strolch.model.timevalue.IValueChange; + +/** + * @author martin_smock + */ +@SuppressWarnings("rawtypes") +public class ValueChange implements IValueChange { + + protected final Long time; + protected final T value; + + /** + * @param time + * @param value + */ + public ValueChange(final Long time, final T value) { + this.time = time; + this.value = value; + } + + @Override + public Long getTime() { + return time; + } + + @Override + @SuppressWarnings("unchecked") + public T getValue() { + return (T) value.getCopy(); + } + + @Override + @SuppressWarnings("unchecked") + public IValueChange getInverse() { + return new ValueChange(time, value.getInverse()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ValueChange other = (ValueChange) obj; + if (time == null) { + if (other.time != null) + return false; + } else if (!time.equals(other.time)) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((time == null) ? 0 : time.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public String toString() { + return "ValueChange [time=" + time + ", value=" + value + "]"; + } + +} diff --git a/src/test/java/li/strolch/model/timedstate/TimeStateTest.java b/src/test/java/li/strolch/model/timedstate/TimeStateTest.java new file mode 100644 index 000000000..f2525094c --- /dev/null +++ b/src/test/java/li/strolch/model/timedstate/TimeStateTest.java @@ -0,0 +1,96 @@ +package li.strolch.model.timedstate; + +import junit.framework.Assert; +import li.strolch.model.timedstate.ITimedState; +import li.strolch.model.timedstate.TimedState; +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.IValueChange; +import li.strolch.model.timevalue.impl.DoubleValue; +import li.strolch.model.timevalue.impl.ValueChange; + +import org.junit.Before; +import org.junit.Test; + +public class TimeStateTest { + + private ITimedState state = new TimedState(); + + final DoubleValue expectedValue1 = new DoubleValue(Double.valueOf(100D)); + final DoubleValue expectedValue2 = new DoubleValue(Double.valueOf(200D)); + + final Long t0 = Long.valueOf(0); + final Long t10 = Long.valueOf(10); + final Long t20 = Long.valueOf(20); + final Long t30 = Long.valueOf(30); + final Long t100 = Long.valueOf(100); + + @Before + public void before() { + + final IValueChange change1 = new ValueChange(t10, expectedValue1); + state.applyChange(change1); + + final ITimeValue stateAt9 = state.getStateAt(9L); + Assert.assertNull(stateAt9); + + final ITimeValue stateAt11 = state.getStateAt(11L); + Assert.assertNotNull(stateAt11); + Assert.assertEquals(true, stateAt11.getValue().matches(expectedValue1)); + + final IValueChange change2 = new ValueChange(t30, expectedValue1); + state.applyChange(change2); + + final ITimeValue stateAt31 = state.getStateAt(31L); + Assert.assertNotNull(stateAt31); + Assert.assertEquals(true, stateAt31.getValue().matches(expectedValue2)); + } + + @Test + public void testGetNextMatch() { + + ITimeValue nextMatch = state.getNextMatch(t0, expectedValue1); + Assert.assertNotNull(nextMatch); + Assert.assertEquals(t10, nextMatch.getTime()); + + nextMatch = state.getNextMatch(t20, expectedValue1); + Assert.assertNull(nextMatch); + + nextMatch = state.getNextMatch(t20, expectedValue2); + Assert.assertNotNull(nextMatch); + Assert.assertEquals(t30, nextMatch.getTime()); + + nextMatch = state.getNextMatch(t30, expectedValue2); + Assert.assertNotNull(nextMatch); + Assert.assertEquals(t30, nextMatch.getTime()); + + nextMatch = state.getNextMatch(t100, expectedValue1); + Assert.assertNull(nextMatch); + + nextMatch = state.getNextMatch(t100, expectedValue2); + Assert.assertNull(nextMatch); + + } + + @Test + public void testGetPreviousMatch() { + + ITimeValue previousMatch = state.getPreviousMatch(t100, expectedValue2); + Assert.assertNotNull(previousMatch); + Assert.assertEquals(t30, previousMatch.getTime()); + + previousMatch = state.getPreviousMatch(t30, expectedValue2); + Assert.assertNull(previousMatch); + + previousMatch = state.getPreviousMatch(t20, expectedValue2); + Assert.assertNull(previousMatch); + + previousMatch = state.getPreviousMatch(t20, expectedValue1); + Assert.assertNotNull(previousMatch); + Assert.assertEquals(t10, previousMatch.getTime()); + + previousMatch = state.getPreviousMatch(t10, expectedValue1); + Assert.assertNull(previousMatch); + + } + +} diff --git a/src/test/java/li/strolch/model/timevalue/FloatTimeVariableTest.java b/src/test/java/li/strolch/model/timevalue/FloatTimeVariableTest.java new file mode 100644 index 000000000..e8fe45670 --- /dev/null +++ b/src/test/java/li/strolch/model/timevalue/FloatTimeVariableTest.java @@ -0,0 +1,137 @@ +package li.strolch.model.timevalue; + +import java.util.Collection; +import java.util.SortedSet; + +import junit.framework.Assert; +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.IValue; +import li.strolch.model.timevalue.IValueChange; +import li.strolch.model.timevalue.impl.DoubleValue; +import li.strolch.model.timevalue.impl.TimeVariable; +import li.strolch.model.timevalue.impl.ValueChange; + +import org.junit.Before; +import org.junit.Test; + +public class FloatTimeVariableTest { + + private static final Long MAX = 100L; + private static final Long STEP = 10L; + private static final Long PICK = 50L; + + private TimeVariable timeVariable; + + /** + * set the values ascending with a difference of STEP + */ + @Before + public void init() { + timeVariable = new TimeVariable(); + for (long i = 0; i < MAX; i += STEP) { + timeVariable.setValueAt(Long.valueOf(i), new DoubleValue(i)); + } + } + + @Test + public void testGetValueAt() { + ITimeValue valueAt = timeVariable.getValueAt(PICK); + Assert.assertEquals(PICK.doubleValue(), valueAt.getValue().getValue()); + } + + /** + * test, that the future values start with the PICK time and are ascending + */ + @Test + public void testGetFutureValues() { + Collection> futureValues = timeVariable.getFutureValues(PICK); + Long expectedTime = PICK; + Double expectedValue = PICK.doubleValue(); + for (ITimeValue value : futureValues) { + Assert.assertEquals(expectedTime, value.getTime()); + Assert.assertTrue(value.getValue().matches(new DoubleValue(expectedValue))); + expectedTime += STEP; + expectedValue += STEP.doubleValue(); + } + } + + /** + * test, that the past values time fields start with 0 and are strictly + * smaller than PICK + */ + @Test + public void testGetPastValues() { + Collection> pastValues = timeVariable.getPastValues(MAX); + Long expectedTime = 0L; + Double expectedValue = expectedTime.doubleValue(); + for (ITimeValue value : pastValues) { + Assert.assertEquals(expectedTime, value.getTime()); + Assert.assertTrue(value.getValue().matches(new DoubleValue(expectedValue))); + expectedTime += STEP; + expectedValue += STEP.doubleValue(); + } + } + + /** + * apply a change and check that the future values are all changed + */ + @Test + public void testApplyChange() { + + DoubleValue doubleValue = new DoubleValue(STEP.doubleValue()); + + IValueChange change = new ValueChange(PICK, doubleValue); + timeVariable.applyChange(change); + + Collection> futureValues = timeVariable.getFutureValues(PICK); + Long expectedTime = PICK; + + IValue expectedValue = new DoubleValue(PICK.doubleValue() + change.getValue().getValue()); + + for (ITimeValue value : futureValues) { + Assert.assertEquals(expectedTime, value.getTime()); + Assert.assertTrue(expectedValue.matches(value.getValue())); + expectedTime += STEP; + expectedValue = expectedValue.add(STEP.doubleValue()); + } + } + + /** + * apply a change to an empty time variable + */ + @Test + public void testApply2Change() { + + timeVariable = new TimeVariable(); + + DoubleValue doubleValue = new DoubleValue(STEP.doubleValue()); + + IValueChange change = new ValueChange(PICK, doubleValue); + timeVariable.applyChange(change); + + ITimeValue actual = timeVariable.getValueAt(PICK); + Assert.assertNotNull(actual); + + IValue expectedValue = new DoubleValue(STEP.doubleValue()); + Assert.assertEquals(true, actual.getValue().matches(expectedValue)); + } + + /** + * test that successors matching the values of their predecessors are + * removed + */ + @Test + public void testCompact() { + + timeVariable = new TimeVariable(); + for (Long i = 0L; i < MAX; i += STEP) { + timeVariable.setValueAt(i, new DoubleValue(STEP.doubleValue())); + } + // call + timeVariable.compact(); + // check + SortedSet> futureValues = timeVariable.getFutureValues(0L); + Assert.assertEquals(1, futureValues.size()); + } + +} diff --git a/src/test/java/li/strolch/model/timevalue/IntegerTimeVariableTest.java b/src/test/java/li/strolch/model/timevalue/IntegerTimeVariableTest.java new file mode 100644 index 000000000..9af523bb1 --- /dev/null +++ b/src/test/java/li/strolch/model/timevalue/IntegerTimeVariableTest.java @@ -0,0 +1,131 @@ +package li.strolch.model.timevalue; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; + +import junit.framework.Assert; +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.IValue; +import li.strolch.model.timevalue.IValueChange; +import li.strolch.model.timevalue.impl.IntegerValue; +import li.strolch.model.timevalue.impl.TimeVariable; +import li.strolch.model.timevalue.impl.ValueChange; + +import org.junit.Before; +import org.junit.Test; + +/** + * Basic tests for a {@link TimeVariable} with integer values. + * + * @author martin_smock + */ +public class IntegerTimeVariableTest { + + private static final Long MAX = 100L; + private static final Integer STEP = 10; + private static final Long PICK = 50L; + + private TimeVariable timeVariable; + private Map expectedValues = new HashMap(); + + /** + * set the values ascending with a difference of STEP + */ + @Before + public void init() { + timeVariable = new TimeVariable(); + for (int i = 0; i < MAX; i += STEP) { + IntegerValue expectedValue = new IntegerValue(i); + Long time = Long.valueOf(i); + expectedValues.put(time, expectedValue); + timeVariable.setValueAt(time, expectedValue); + } + } + + @Test + public void testGetValueAt() { + ITimeValue valueAt = timeVariable.getValueAt(PICK); + Assert.assertEquals(expectedValues.get(PICK), valueAt.getValue()); + } + + /** + * test, that the future values start with the PICK time and are ascending + */ + @Test + public void testGetFutureValues() { + Collection> futureValues = timeVariable.getFutureValues(PICK); + Long expectedTime = PICK; + Integer expectedValue = PICK.intValue(); + for (ITimeValue value : futureValues) { + Assert.assertEquals(expectedTime, value.getTime()); + Assert.assertTrue(value.getValue().matches(new IntegerValue(expectedValue))); + expectedTime += STEP; + expectedValue += STEP.intValue(); + } + } + + /** + * test, that the past values time fields start with 0 and are strictly + * smaller than PICK + */ + @Test + public void testGetPastValues() { + Collection> pastValues = timeVariable.getPastValues(MAX); + Long expectedTime = 0L; + Integer expectedValue = expectedTime.intValue(); + for (ITimeValue value : pastValues) { + Assert.assertEquals(expectedTime, value.getTime()); + Assert.assertTrue(value.getValue().matches(new IntegerValue(expectedValue))); + expectedTime += STEP; + expectedValue += STEP.intValue(); + } + } + + /** + * apply a change and check that the future values are all changed + */ + @Test + public void testApplyChange() { + + IntegerValue integerValue = new IntegerValue(STEP.intValue()); + + IValueChange change = new ValueChange(PICK, integerValue); + timeVariable.applyChange(change); + + Collection> futureValues = timeVariable.getFutureValues(PICK); + Long expectedTime = PICK; + + IValue expectedValue = new IntegerValue(PICK.intValue() + change.getValue().getValue()); + for (ITimeValue value : futureValues) { + Assert.assertEquals(expectedTime, value.getTime()); + Assert.assertTrue(expectedValue.matches(value.getValue())); + expectedTime += STEP; + expectedValue = expectedValue.add(STEP.intValue()); + } + } + + /** + * test that successors matching the values of their predecessors are + * removed + */ + @Test + public void testCompact() { + timeVariable = new TimeVariable(); + for (Long i = 0L; i < MAX; i += STEP) { + timeVariable.setValueAt(i, new IntegerValue(STEP.intValue())); + } + + // call + timeVariable.compact(); + + // check + SortedSet> futureValues = timeVariable.getFutureValues(0L); + Assert.assertEquals(1, futureValues.size()); + + ITimeValue next = futureValues.iterator().next(); + Assert.assertEquals(Long.valueOf(0), next.getTime()); + } + +} diff --git a/src/test/java/li/strolch/model/timevalue/StringTimeVariableTest.java b/src/test/java/li/strolch/model/timevalue/StringTimeVariableTest.java new file mode 100644 index 000000000..031d2de0e --- /dev/null +++ b/src/test/java/li/strolch/model/timevalue/StringTimeVariableTest.java @@ -0,0 +1,118 @@ +package li.strolch.model.timevalue; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import junit.framework.Assert; +import li.strolch.model.timevalue.ITimeValue; +import li.strolch.model.timevalue.IValue; +import li.strolch.model.timevalue.IValueChange; +import li.strolch.model.timevalue.impl.AString; +import li.strolch.model.timevalue.impl.StringSetValue; +import li.strolch.model.timevalue.impl.TimeVariable; +import li.strolch.model.timevalue.impl.ValueChange; + +import org.junit.Before; +import org.junit.Test; + +public class StringTimeVariableTest { + + private static final Long MAX = 100L; + private static final Long STEP = 10L; + private static final Long PICK = 50L; + + private TimeVariable>> timeVariable; + + private Map testSets = new HashMap(); + + @Before + public void init() { + timeVariable = new TimeVariable>>(); + for (Long i = 0L; i < MAX; i += STEP) { + Set testSet = new HashSet(); + StringSetValue testValue = new StringSetValue(testSet); + testSets.put(i, testValue); + testSet.add(new AString("string " + i)); + timeVariable.setValueAt(i, new StringSetValue(testSet)); + } + } + + @Test + public void testGetValueAt() { + ITimeValue>> valueAt = timeVariable.getValueAt(PICK); + Assert.assertEquals(true, valueAt.getValue().matches(testSets.get(PICK))); + } + + @Test + public void testGetFutureValues() { + Collection>>> futureValues = timeVariable.getFutureValues(PICK); + for (ITimeValue>> iTimeValue : futureValues) { + Long time = iTimeValue.getTime(); + Assert.assertEquals(true, time >= PICK); + Assert.assertNotNull(iTimeValue.getValue()); + Assert.assertEquals(true, iTimeValue.getValue().matches(testSets.get(time))); + } + } + + @Test + public void testGetPastValues() { + Collection>>> pastValues = timeVariable.getPastValues(PICK); + for (ITimeValue>> iTimeValue : pastValues) { + Long time = iTimeValue.getTime(); + Assert.assertEquals(true, time < PICK); + Assert.assertNotNull(iTimeValue.getValue()); + Assert.assertEquals(true, iTimeValue.getValue().matches(testSets.get(time))); + } + } + + @Test + public void testApplyChange() { + + Set testSet = new HashSet(); + testSet.add(new AString("Martin")); + StringSetValue testValue = new StringSetValue(testSet); + + timeVariable = new TimeVariable>>(); + timeVariable.setValueAt(PICK, testValue); + + IValue> inverseTestValue = testValue.getInverse(); + IValueChange>> change = new ValueChange>>(PICK, inverseTestValue); + timeVariable.applyChange(change); + + // check the future values + Collection>>> futureValues = timeVariable.getFutureValues(0L); + for (ITimeValue>> iTimeValue : futureValues) { + System.out.println("++ " + iTimeValue); + } + + Assert.assertEquals(1, futureValues.size()); // a empty one is left + + } + + @Test + public void testCompact() { + + timeVariable = new TimeVariable>>(); + for (Long i = 0L; i < MAX; i += STEP) { + Set testSet = new HashSet(); + StringSetValue testValue = new StringSetValue(testSet); + testSets.put(i, testValue); + testSet.add(new AString("same string")); + timeVariable.setValueAt(i, new StringSetValue(testSet)); + } + + SortedSet>>> valuesInitial = timeVariable.getFutureValues(0L); + Assert.assertEquals(true, valuesInitial.size() > 1); + + timeVariable.compact(); + + SortedSet>>> valuesCompacted = timeVariable.getFutureValues(0L); + Assert.assertEquals(1, valuesCompacted.size()); + + } + +} diff --git a/src/test/java/li/strolch/model/timevalue/ValueTests.java b/src/test/java/li/strolch/model/timevalue/ValueTests.java new file mode 100644 index 000000000..072262fe6 --- /dev/null +++ b/src/test/java/li/strolch/model/timevalue/ValueTests.java @@ -0,0 +1,81 @@ +package li.strolch.model.timevalue; + +import static org.junit.Assert.assertEquals; + +import java.util.HashSet; +import java.util.Set; + +import li.strolch.model.timevalue.IValue; +import li.strolch.model.timevalue.impl.AString; +import li.strolch.model.timevalue.impl.DoubleValue; +import li.strolch.model.timevalue.impl.IntegerValue; +import li.strolch.model.timevalue.impl.StringSetValue; + +import org.junit.Test; + +public class ValueTests { + + /** + * check, that adding the inverse results in the neutral element (=0) + */ + @Test + public void testDoubleInverse() { + DoubleValue value = new DoubleValue(10.0d); + DoubleValue inverse = value.getInverse(); + assertEquals(Double.valueOf(-10.0d), inverse.getValue()); + assertEquals(Double.valueOf(0), value.add(inverse.getValue()) + .getValue()); + } + + /** + * check, that adding the inverse results in the neutral element (=0) + */ + @Test + public void testIntegerInverse() { + IntegerValue value = new IntegerValue(10); + IntegerValue inverse = value.getInverse(); + assertEquals(Integer.valueOf(-10), inverse.getValue()); + assertEquals(Integer.valueOf(0), value.add(inverse.getValue()) + .getValue()); + } + + /** + * check, that adding the inverse results in the neutral element (empty Set) + */ + @Test + public void testStringSetInverse() { + Set aStrings = new HashSet(); + for (int i = 0; i < 10; i++) { + aStrings.add(new AString("string " + i)); + } + IValue> value = new StringSetValue(aStrings); + IValue> inverse = value.getInverse(); + assertEquals(true, value.matches(inverse.getInverse())); + assertEquals(0, value.add(inverse.getValue()).getValue().size()); + } + + /** + * check, that the difference left is as expected + */ + @Test + public void testStringSetNearInverse() { + + Set aStrings1 = new HashSet(); + for (int i = 0; i < 10; i++) { + aStrings1.add(new AString("string " + i)); + } + IValue> value1 = new StringSetValue(aStrings1); + + Set aStrings2 = new HashSet(); + for (int i = 0; i < 9; i++) { + aStrings2.add(new AString("string " + i, true)); + } + IValue> value2 = new StringSetValue(aStrings2); + + assertEquals(false, value1.matches(value2)); + assertEquals(1, value1.add(value2.getValue()).getValue().size()); + assertEquals(10, value1.add(value2.getInverse().getValue()).getValue() + .size()); + } + +}