From 7e65ad5fe86ea270fcdd80b91bd3ead2ecee901f Mon Sep 17 00:00:00 2001 From: Ben Steffensmeier Date: Thu, 8 Aug 2013 15:26:52 -0500 Subject: [PATCH] Issue #2245 Make all DataTime comparisons consistent. Change-Id: I1d4bc8c75860f948c6780761cc862f2acb25874d Former-commit-id: 9901e7a622f1230a723c040f2513e6a18d3b58ce [formerly b175ec81e61f18b42432e0245cddb953fbc5963f] Former-commit-id: 479f72bc10ec793f0ef8f6a36d4ae5f639738c67 --- .../uf/viz/d2d/core/time/TimeMatcher.java | 24 ++- .../raytheon/edex/util/TimeSeriesUtil.java | 66 ------- .../uf/common/time/CombinedDataTime.java | 110 +----------- .../com/raytheon/uf/common/time/DataTime.java | 146 +--------------- .../uf/common/time/DataTimeComparator.java | 165 ++++++++++++++++++ 5 files changed, 187 insertions(+), 324 deletions(-) delete mode 100644 edexOsgi/com.raytheon.edex.common/src/com/raytheon/edex/util/TimeSeriesUtil.java create mode 100644 edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTimeComparator.java diff --git a/cave/com.raytheon.uf.viz.d2d.core/src/com/raytheon/uf/viz/d2d/core/time/TimeMatcher.java b/cave/com.raytheon.uf.viz.d2d.core/src/com/raytheon/uf/viz/d2d/core/time/TimeMatcher.java index ac40e19264..4ee0341731 100644 --- a/cave/com.raytheon.uf.viz.d2d.core/src/com/raytheon/uf/viz/d2d/core/time/TimeMatcher.java +++ b/cave/com.raytheon.uf.viz.d2d.core/src/com/raytheon/uf/viz/d2d/core/time/TimeMatcher.java @@ -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. * * * @@ -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; diff --git a/edexOsgi/com.raytheon.edex.common/src/com/raytheon/edex/util/TimeSeriesUtil.java b/edexOsgi/com.raytheon.edex.common/src/com/raytheon/edex/util/TimeSeriesUtil.java deleted file mode 100644 index 9359413823..0000000000 --- a/edexOsgi/com.raytheon.edex.common/src/com/raytheon/edex/util/TimeSeriesUtil.java +++ /dev/null @@ -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 - * - *
- * SOFTWARE HISTORY
- * Date         Ticket#    Engineer    Description
- * ------------ ---------- ----------- --------------------------
- * Nov 20, 2007            njensen     Initial creation
- * 
- * 
- * - * @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 map = new HashMap(); - 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; - } - -} diff --git a/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/CombinedDataTime.java b/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/CombinedDataTime.java index 3e9579fb23..a7113b63b6 100644 --- a/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/CombinedDataTime.java +++ b/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/CombinedDataTime.java @@ -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. * *
  * 
@@ -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.
  * 
  * 
* @@ -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; - - } } diff --git a/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTime.java b/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTime.java index deaeffb499..dbf07d8f9f 100644 --- a/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTime.java +++ b/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTime.java @@ -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. * * * @@ -119,18 +123,8 @@ public class DataTime implements Comparable, 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 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, 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, 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, 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, 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) { diff --git a/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTimeComparator.java b/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTimeComparator.java new file mode 100644 index 0000000000..b4f167de46 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTimeComparator.java @@ -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. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Aug 8, 2013  2245       bsteffen    Initial creation
+ * 
+ * 
+ * + * @author bsteffen + * @version 1.0 + */ + +public class DataTimeComparator implements Comparator { + + /** 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.
+ * 2. Longer periods are greater than shorter periods
+ * 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()); + } + +}