Issue #2245 Make all DataTime comparisons consistent.

Change-Id: I1d4bc8c75860f948c6780761cc862f2acb25874d

Former-commit-id: b175ec81e61f18b42432e0245cddb953fbc5963f
This commit is contained in:
Ben Steffensmeier 2013-08-08 15:26:52 -05:00
parent 2245b2f7a6
commit 9901e7a622
5 changed files with 187 additions and 324 deletions

View file

@ -38,6 +38,7 @@ import org.apache.commons.collections.map.MultiValueMap;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.DataTime.FLAG;
import com.raytheon.uf.common.time.DataTimeComparator;
/**
*
@ -48,7 +49,9 @@ import com.raytheon.uf.common.time.DataTime.FLAG;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 19, 2007 chammack Initial Creation.
* May 31, 2013 DR 15908 dhuffman Removed a null from a method call to cease a null pointer exception.
* May 31, 2013 15908 dhuffman Removed a null from a method call to
* cease a null pointer exception.
* Aug 08, 2013 2245 bsteffen Make all DataTime comparisons consistent.
*
* </pre>
*
@ -177,14 +180,9 @@ public class TimeMatcher {
return;
}
for (DataTime time : times) {
if (time != null) {
time.setSortKeys(DataTime.SortKey.VALID_TIME,
DataTime.SortKey.FORECAST_TIME, true);
}
}
Collections.sort(times);
Collections.sort(times, new DataTimeComparator(
DataTimeComparator.SortKey.VALID_TIME,
DataTimeComparator.SortKey.FORECAST_TIME, true));
if (majorIndex == null) {
return;
@ -896,11 +894,9 @@ public class TimeMatcher {
return;
}
for (DataTime t : times) {
t.setSortKeys(DataTime.SortKey.INITIAL_TIME,
DataTime.SortKey.FORECAST_TIME, true);
}
Collections.sort(times);
Collections.sort(times, new DataTimeComparator(
DataTimeComparator.SortKey.INITIAL_TIME,
DataTimeComparator.SortKey.FORECAST_TIME, true));
if (majorIndex == null) {
return;

View file

@ -1,66 +0,0 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.edex.util;
import java.util.Arrays;
import java.util.HashMap;
import com.raytheon.uf.common.dataplugin.PluginDataObject;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.DataTime.SortKey;
/**
* Utility methods pertaining to time
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Nov 20, 2007 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class TimeSeriesUtil {
public static PluginDataObject[] sortByTime(PluginDataObject[] unsorted,
SortKey majorKey, SortKey minorKey) {
int size = unsorted.length;
PluginDataObject[] sorted = new PluginDataObject[size];
DataTime[] dt = new DataTime[size];
HashMap<DataTime, PluginDataObject> map = new HashMap<DataTime, PluginDataObject>();
for (int i = 0; i < size; i++) {
DataTime time = unsorted[i].getDataTime();
time.setSortKeys(majorKey, minorKey, false);
dt[i] = time;
map.put(time, unsorted[i]);
}
Arrays.sort(dt);
for (int i = 0; i < size; i++) {
sorted[i] = map.get(dt[i]);
}
return sorted;
}
}

View file

@ -7,7 +7,8 @@ import java.util.TimeZone;
import javax.persistence.Transient;
/**
* TODO Add Description
* A single DataTime object representing 2 DataTimes, useful for products which
* are a combination of other products with potentially different times.
*
* <pre>
*
@ -16,6 +17,7 @@ import javax.persistence.Transient;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 10, 2011 rgeorge Initial creation
* Aug 08, 2013 2245 bsteffen Make all DataTime comparisons consistent.
*
* </pre>
*
@ -45,8 +47,6 @@ public class CombinedDataTime extends DataTime {
this.primaryDataTime = primaryDataTime;
this.fcstTime = primaryDataTime.fcstTime;
this.levelValue = primaryDataTime.levelValue;
this.majorKey = primaryDataTime.majorKey;
this.minorKey = primaryDataTime.minorKey;
this.refTime = primaryDataTime.refTime;
this.utilityFlags = primaryDataTime.utilityFlags;
this.validPeriod = new TimeRange(this.refTime,
@ -88,8 +88,6 @@ public class CombinedDataTime extends DataTime {
result = prime * result + fcstTime;
result = prime * result
+ ((levelValue == null) ? 0 : levelValue.hashCode());
result = prime * result + ((majorKey == null) ? 0 : majorKey.ordinal());
result = prime * result + ((minorKey == null) ? 0 : minorKey.ordinal());
if (utilityFlags == null) {
result = prime * result + 0;
} else {
@ -248,106 +246,4 @@ public class CombinedDataTime extends DataTime {
}
}
/**
* Returns true if the left hand side is greater than the right hand side
*
* @param rhs
* the right hand side
* @return true if left hand side is greater than
*/
public boolean greaterThan(DataTime rhs) {
if (rhs.getRefTime() == null) {
return (fcstTime > rhs.getFcstTime());
} else {
if (matchMode) {
switch (majorKey) {
case INITIAL_TIME:
if (getMatchRef() > rhs.getMatchRef())
return true;
if (getMatchRef() < rhs.getMatchRef())
return false;
break;
case FORECAST_TIME:
if (getRefTime().getTime() == 0)
return false;
if (getMatchFcst() > rhs.getMatchFcst())
return true;
if (getMatchFcst() < rhs.getMatchFcst())
return false;
break;
case VALID_TIME:
if (getMatchValid() > rhs.getMatchValid())
return true;
if (getMatchValid() < rhs.getMatchValid())
return false;
}
switch (minorKey) {
case INITIAL_TIME:
if (getMatchRef() > rhs.getMatchRef())
return true;
if (getMatchRef() < rhs.getMatchRef())
return false;
break;
case FORECAST_TIME:
if (getMatchFcst() > rhs.getMatchFcst())
return true;
if (getMatchFcst() < rhs.getMatchFcst())
return false;
break;
case VALID_TIME:
if (getMatchFcst() > rhs.getMatchFcst())
return true;
if (getMatchFcst() < rhs.getMatchFcst())
return false;
}
if (getLevelValue() > rhs.getLevelValue()) {
return true;
}
} else {
switch (majorKey) {
case INITIAL_TIME:
if (refTime.getTime() > rhs.refTime.getTime())
return true;
if (refTime.getTime() < rhs.refTime.getTime())
return false;
break;
case FORECAST_TIME:
if (fcstTime > rhs.fcstTime)
return true;
if (fcstTime < rhs.fcstTime)
return false;
break;
case VALID_TIME:
if (getValidTime().getTimeInMillis() > rhs.getValidTime()
.getTimeInMillis())
return true;
if (getValidTime().getTimeInMillis() < rhs.getValidTime()
.getTimeInMillis())
return false;
if (refTime.getTime() > rhs.getRefTime().getTime())
return true;
if (refTime.getTime() < rhs.getRefTime().getTime())
return false;
}
switch (minorKey) {
case INITIAL_TIME:
if (refTime.getTime() > rhs.refTime.getTime())
return true;
break;
case FORECAST_TIME:
if (fcstTime > rhs.fcstTime)
return true;
break;
case VALID_TIME:
if (refTime.getTime() + (fcstTime * 1000) > rhs.refTime
.getTime() + (rhs.fcstTime * 1000))
return true;
}
}
}
return false;
}
}

View file

@ -24,6 +24,7 @@ import java.io.Serializable;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.EnumSet;
import java.util.TimeZone;
@ -46,6 +47,7 @@ import org.hibernate.annotations.Type;
import com.raytheon.uf.common.serialization.ISerializableObject;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
import com.raytheon.uf.common.time.DataTimeComparator.SortKey;
import com.raytheon.uf.common.time.util.CalendarConverter;
import com.raytheon.uf.common.time.util.TimeUtil;
@ -60,8 +62,10 @@ import com.raytheon.uf.common.time.util.TimeUtil;
* ------------ ---------- ----------- --------------------------
* Jim Ramer Original Code
* Jun 18, 2007 chammack Partial port to Java
* Apr 12, 2013 1857 bgonzale Added Index annotations to getter methods.
* Apr 12, 2013 1857 bgonzale Added Index annotations to getter
* methods.
* Mar 02, 2013 1970 bgonzale Removed Index annotations.
* Aug 08, 2013 2245 bsteffen Make all DataTime comparisons consistent.
*
* </pre>
*
@ -119,18 +123,8 @@ public class DataTime implements Comparable<DataTime>, Serializable,
*/
private static final long serialVersionUID = 1L;
/** Defines possible time sort keys */
public static enum SortKey {
INITIAL_TIME, FORECAST_TIME, VALID_TIME
};
/** The major sort key */
@Transient
protected SortKey majorKey = SortKey.VALID_TIME;
/** The minor sort key */
@Transient
protected SortKey minorKey = SortKey.FORECAST_TIME;
private static final Comparator<DataTime> DEFAULT_COMPARATOR = new DataTimeComparator(
SortKey.VALID_TIME, SortKey.FORECAST_TIME, false);
/** The reference time */
@Column(name = "refTime")
@ -150,10 +144,6 @@ public class DataTime implements Comparable<DataTime>, Serializable,
@XmlElement
protected TimeRange validPeriod;
/** Is data to be sorted using match mode */
@Transient
protected boolean matchMode;
@Transient
protected boolean visible = true;
@ -452,26 +442,6 @@ public class DataTime implements Comparable<DataTime>, Serializable,
}
}
/**
* This routine determines which characteristics of a DataTime object,
* reference time, valid time, or forecast time, affect how relational
* operators >, <, >=, and <= behave. Default is to sort primarily on the
* valid time and secondarily on the reference time.
*
* @param majorKey
* the major sort key
* @param minorKey
* the minor sort key
* @param matchMode
* the match mode flag
*/
public void setSortKeys(SortKey majorKey, SortKey minorKey,
boolean matchMode) {
this.majorKey = majorKey;
this.minorKey = minorKey;
this.matchMode = matchMode;
}
/**
* Returns true if the left hand side is greater than the right hand side
*
@ -480,99 +450,7 @@ public class DataTime implements Comparable<DataTime>, Serializable,
* @return true if left hand side is greater than
*/
public boolean greaterThan(DataTime rhs) {
if (rhs.getRefTime() == null) {
return (fcstTime > rhs.getFcstTime());
} else {
if (matchMode) {
switch (majorKey) {
case INITIAL_TIME:
if (getMatchRef() > rhs.getMatchRef())
return true;
if (getMatchRef() < rhs.getMatchRef())
return false;
break;
case FORECAST_TIME:
if (getRefTime().getTime() == 0)
return false;
if (getMatchFcst() > rhs.getMatchFcst())
return true;
if (getMatchFcst() < rhs.getMatchFcst())
return false;
break;
case VALID_TIME:
if (getMatchValid() > rhs.getMatchValid())
return true;
if (getMatchValid() < rhs.getMatchValid())
return false;
}
switch (minorKey) {
case INITIAL_TIME:
if (getMatchRef() > rhs.getMatchRef())
return true;
if (getMatchRef() < rhs.getMatchRef())
return false;
break;
case FORECAST_TIME:
if (getMatchFcst() > rhs.getMatchFcst())
return true;
if (getMatchFcst() < rhs.getMatchFcst())
return false;
break;
case VALID_TIME:
if (getMatchFcst() > rhs.getMatchFcst())
return true;
if (getMatchFcst() < rhs.getMatchFcst())
return false;
}
if (getLevelValue() > rhs.getLevelValue()) {
return true;
}
} else {
switch (majorKey) {
case INITIAL_TIME:
if (refTime.getTime() > rhs.refTime.getTime())
return true;
if (refTime.getTime() < rhs.refTime.getTime())
return false;
break;
case FORECAST_TIME:
if (fcstTime > rhs.fcstTime)
return true;
if (fcstTime < rhs.fcstTime)
return false;
break;
case VALID_TIME:
if (refTime.getTime() + (((long) fcstTime) * 1000) > rhs.refTime
.getTime() + (((long) rhs.fcstTime) * 1000))
return true;
if (refTime.getTime() + (((long) fcstTime) * 1000) < rhs.refTime
.getTime() + (((long) rhs.fcstTime) * 1000))
return false;
if (refTime.getTime() > rhs.getRefTime().getTime())
return true;
if (refTime.getTime() < rhs.getRefTime().getTime())
return false;
}
switch (minorKey) {
case INITIAL_TIME:
if (refTime.getTime() > rhs.refTime.getTime())
return true;
break;
case FORECAST_TIME:
if (fcstTime > rhs.fcstTime)
return true;
break;
case VALID_TIME:
if (refTime.getTime() + (((long) fcstTime) * 1000) > rhs.refTime
.getTime() + (((long) rhs.fcstTime) * 1000))
return true;
}
}
}
return false;
return compareTo(rhs) > 0;
}
/**
@ -687,13 +565,7 @@ public class DataTime implements Comparable<DataTime>, Serializable,
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(DataTime o) {
if (this.equals(o))
return 0;
if (this.greaterThan(o))
return 1;
return -1;
return DEFAULT_COMPARATOR.compare(this, o);
}
public void setRefTime(Date refTime) {

View file

@ -0,0 +1,165 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.time;
import java.util.Comparator;
/**
* Provides configurable comparisons of DataTimes. This can which
* characteristics of a DataTime object, reference time, valid time, or forecast
* time, affect how relational operators >, <, >=, and <= behave.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 8, 2013 2245 bsteffen Initial creation
*
* </pre>
*
* @author bsteffen
* @version 1.0
*/
public class DataTimeComparator implements Comparator<DataTime> {
/** Defines possible time sort keys */
public static enum SortKey {
INITIAL_TIME, FORECAST_TIME, VALID_TIME
};
/** The major sort key */
protected final SortKey majorKey;
/** The minor sort key */
protected final SortKey minorKey;
/** Is data to be sorted using match mode */
protected final boolean matchMode;
/**
* This routine determines which characteristics of a DataTime object,
* reference time, valid time, or forecast time, affect how relational
* operators >, <, >=, and <= behave.
*
* @param majorKey
* the major sort key
* @param minorKey
* the minor sort key
* @param matchMode
* the match mode flag
*/
public DataTimeComparator(SortKey majorKey, SortKey minorKey,
boolean matchMode) {
this.majorKey = majorKey;
this.minorKey = minorKey;
this.matchMode = matchMode;
}
@Override
public int compare(DataTime time1, DataTime time2) {
int result = compare(majorKey, time1, time2);
if (result != 0) {
return result;
}
result = compare(minorKey, time1, time2);
if (result != 0) {
return result;
}
result = compareLevel(time1, time2);
if (result != 0) {
return result;
}
return compareValidPeriod(time1, time2);
}
private int compare(SortKey sortKey, DataTime time1, DataTime time2) {
if (matchMode) {
return compareMatch(sortKey, time1, time2);
} else {
return compareNoMatch(sortKey, time1, time2);
}
}
private int compareNoMatch(SortKey sortKey, DataTime time1, DataTime time2) {
switch (sortKey) {
case INITIAL_TIME:
return Long.compare(time1.getRefTime().getTime(), time2
.getRefTime().getTime());
case FORECAST_TIME:
return Integer.compare(time1.getFcstTime(), time2.getFcstTime());
case VALID_TIME:
return Long.compare(time1.getValidTime().getTimeInMillis(), time2
.getValidTime().getTimeInMillis());
default:
throw new IllegalArgumentException(String.valueOf(sortKey)
+ " is not a recognized SortKey.");
}
}
private int compareMatch(SortKey sortKey, DataTime time1, DataTime time2) {
switch (sortKey) {
case INITIAL_TIME:
return Long.compare(time1.getMatchRef(), time2.getMatchRef());
case FORECAST_TIME:
return Long.compare(time1.getMatchFcst(), time2.getMatchFcst());
case VALID_TIME:
return Long.compare(time1.getMatchValid(), time2.getMatchValid());
default:
throw new IllegalArgumentException(String.valueOf(sortKey)
+ " is not a recognized SortKey.");
}
}
private int compareLevel(DataTime time1, DataTime time2) {
return Double.compare(time1.getLevelValue(), time2.getLevelValue());
}
/**
* For valid period the rules arbitrary but we need some rules to make the
* comparator consistent.
*
* 1. Not null periods are greater than null periods. <br />
* 2. Longer periods are greater than shorter periods <br />
* 3. Periods that start later are greater than periods that start earlier.
*/
private int compareValidPeriod(DataTime time1, DataTime time2) {
TimeRange p1 = time1.getValidPeriod();
TimeRange p2 = time2.getValidPeriod();
if (p1 == p2) {
return 0;
} else if (p1 == null) {
return -1;
} else if (p2 == null) {
return 1;
}
int result = Long.compare(p1.getDuration(), p2.getDuration());
if (result != 0) {
return result;
}
return Long.compare(p1.getStart().getTime(), p2.getStart().getTime());
}
}