Skip to content

Commit

Permalink
added documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
pmeisen committed Mar 14, 2017
1 parent 4166655 commit 4701345
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 53 deletions.
60 changes: 58 additions & 2 deletions docs/Interval.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ new Interval<>(1L, 10L).contains(10.0) == true
new Interval<>(Long.class, 1L, 2L, false, true).contains(2) == false;
```


The `compareTo` method validates, if an interval is smaller
(`< 0`), equal (`== 0`), or larger (`> 0`) than another interval. The method allows to use a different types interval to
compare with, e.g.,
Expand All @@ -67,4 +66,61 @@ new Interval<>(1L, 5L).compareTo(new Interval<>(1, 6)) < 0; // i.e., [1, 5

// the specified type, specifies the boundaries
new Interval<>(Double.class, 1.0, 5.0, true, true).compareTo(new Interval<>(Long.class, 1L, 5L, true, true)) > 0; // i.e., (1.0, 5.0) < (1, 5)
```
```

## Extending Intervals... and what to consider

The provided `Interval` implementation implements the `IInterval` interface, which is needed for all other interval-based
structures in this library (e.g., `IntervalTree`). Nevertheless, when working with interval data, it is often needed to have
more than just the interval itself, i.e., an identifier or more complex additional data may be associated with an interval.
This additional data may also affect the similarity of intervals, i.e., let's assume we have the following data (using JSON):

```
[
{ start: 5, end: 10, id: 2 },
{ start: 5, end: 10, id: 3 },
{ start: 5, end: 10, id: 2 }
]
```

The JSON shows three intervals, from which two are equal (`{ start: 5, end: 10, id: 2 }`). The third interval
(`{ start: 5, end: 10, id: 3 }`) is unequal to the others (based on the associated data). The default implementation
`Interval` would not recognize the difference and assume all of the three intervals are equal. To modify the handling,
it is recommended to `extend` the `Interval` implementation (and just add the new comparision).

```java
public class IdInterval<I extends Comparable<I>, T extends Number & Comparable<T>> extends Interval<T> {
private final I id;

/*
* We removed the code for constructors and getters/setters, we also
* did not add the compareId method, which just compares the actual
* identifiers (using the compareTo method of these).
*/

@Override
public int compareTo(final IInterval i) {
final int cmp = super.compareTo(i);

if (cmp == 0) {

// the intervals are equal, so we must use the identifiers
if (i instanceof IdInterval) {
return compareId(IdInterval.class.cast(i));
}
// we don't have any identifiers (the instance is of a different type)
else {
return getClass().getName().compareTo(i.getClass().getName());
}
} else {
return cmp;
}
}
}
```

In this implementation, the most important line is `super.compareTo(i)`. You should always use the `super` implementation,
and only be more general regarding the comparision, if the `super.compareTo` result is `0`. Otherwise, the intervals are
already different and thus, the instances can never be equal. One may argue that the comparison should be based on the identifiers.
This may be correct within specific use-cases, but be aware, that such implementations can not utilize, e.g., interval-based
index structures like the `IntervalTree`.
82 changes: 33 additions & 49 deletions src/com/brein/time/timeintervals/intervals/IdInterval.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
package com.brein.time.timeintervals.intervals;

import java.util.Objects;

public class IdInterval<I extends Comparable<I>, T extends Number & Comparable<T>> extends Interval<T> {
private final I id;

public IdInterval(final I id, final Long start, final Long end) {
//noinspection unchecked
this(id, Long.class, (T) start, (T) end, false, false);
super(start, end);
this.id = id;
}

public IdInterval(final I id, final Integer start, final Integer end) {
//noinspection unchecked
this(id, Integer.class, (T) start, (T) end, false, false);
super(start, end);
this.id = id;
}

public IdInterval(final I id, final Double start, final Double end) {
//noinspection unchecked
this(id, Double.class, (T) start, (T) end, false, false);
super(start, end);
this.id = id;
}

public IdInterval(final I id,
final Class clazz,
final Class<T> clazz,
final T start,
final T end) {
this(id, clazz, start, end, false, false);
}

public IdInterval(final I id,
final Class clazz,
final Class<T> clazz,
final T start,
final T end,
final boolean openStart,
Expand All @@ -41,8 +39,19 @@ public I getId() {
return id;
}

public boolean idEquals(final IdInterval i) {
return i != null && Objects.equals(this.id, i.id);
public int compareId(final IdInterval iId) {
if (this.id == null && iId == null) {
return 0;
} else if (this.id == null) {
return -1;
} else if (iId == null) {
return 1;
} else if (this.id.getClass().isInstance(iId.id)) {
//noinspection unchecked
return this.id.compareTo((I) iId.id);
} else {
return this.id.toString().compareTo(iId.id.toString());
}
}

@Override
Expand All @@ -51,47 +60,22 @@ public IdInterval<I, T> clone() throws CloneNotSupportedException {
}

@Override
@SuppressWarnings("SimplifiableIfStatement")
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj == null) {
return false;
} else if (obj instanceof IdInterval) {
final IdInterval iId = IdInterval.class.cast(obj);
return Objects.equals(this.id, iId.id) && super.equals(iId);
} else if (obj instanceof IInterval) {
return super.equals(obj);
} else {
return false;
}
}

@Override
@SuppressWarnings({"unchecked", "NullableProblems"})
@SuppressWarnings("NullableProblems")
public int compareTo(final IInterval i) {
if (i instanceof IdInterval) {
final IdInterval iId = IdInterval.class.cast(i);
final int cmp = super.compareTo(iId);

if (cmp != 0 || Objects.equals(this.id, iId.id)) {
return cmp;
} else if (this.id.getClass().isInstance(iId.id)) {
return this.id.compareTo((I) iId.id);
} else {
return this.id.toString().compareTo(iId.id.toString());
}
} else {
final int cmp = super.compareTo(i);
final int cmp = super.compareTo(i);

// if they are equal they cannot be, because there is no identifier, so we can only be less or more
if (cmp == 0) {
if (cmp == 0) {

// we don't have any empty identifier, thus we compare the class-names
return this.getClass().getName().compareTo(i.getClass().getName());
} else {
return cmp;
// the intervals are equal, so we must use the identifiers
if (i instanceof IdInterval) {
return compareId(IdInterval.class.cast(i));
}
// we don't have any identifiers (the instance is of a different type)
else {
return getClass().getName().compareTo(i.getClass().getName());
}
} else {
return cmp;
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/com/brein/time/timeintervals/intervals/Interval.java
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,7 @@ public boolean equals(final Object obj) {
} else if (obj == null) {
return false;
} else if (obj instanceof Interval) {
@SuppressWarnings("unchecked")
final Interval<T> i = Interval.class.cast(obj);
final Interval i = Interval.class.cast(obj);
return compareTo(i) == 0;
} else {
return false;
Expand Down

0 comments on commit 4701345

Please sign in to comment.