001/* An event that can be inserted in a CalendarQueue using an instance of Time 002 as a sort key. 003 004 Copyright (c) 1998-2014 The Regents of the University of California. 005 All rights reserved. 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION_2 026 COPYRIGHTENDKEY 027 028 */ 029package ptolemy.actor.util; 030 031/////////////////////////////////////////////////////////////////// 032//// TimedEvent 033 034/** 035 This class aggregates an instance of Time and an Object, and provides a CQComparator 036 as an inner class. 037 038 @author Edward A. Lee and Haiyang Zheng 039 @version $Id$ 040 @since Ptolemy II 0.4 041 @Pt.ProposedRating Yellow (eal) 042 @Pt.AcceptedRating Red (liuj) 043 @see CQComparator 044 @see Time 045 */ 046public class TimedEvent implements Comparable<TimedEvent> { 047 /** Construct an event with the specified time stamp and contents. 048 * @param time The time stamp. 049 * @param obj The contents. 050 */ 051 public TimedEvent(Time time, Object obj) { 052 timeStamp = time; 053 contents = obj; 054 } 055 056 /////////////////////////////////////////////////////////////////// 057 //// public members //// 058 059 /** The time stamp. */ 060 public Time timeStamp; 061 062 /** The event object. */ 063 public Object contents; 064 065 /////////////////////////////////////////////////////////////////// 066 //// public methods //// 067 068 /** Display timeStamp and contents. */ 069 @Override 070 public String toString() { 071 return "timeStamp: " + timeStamp + ", contents: " + contents; 072 } 073 074 /** Return true if this TimedEvent object has the same 075 * timeStamp and eventObject as the given TimedEvent object. 076 * @param timedEvent The TimedEvent object that this 077 * TimedEvent object is compared to. 078 * @return True if the two TimedEvent objects have the same time 079 * stamp and event object. 080 */ 081 @Override 082 public boolean equals(Object timedEvent) { 083 // See http://www.technofundo.com/tech/java/equalhash.html 084 085 /* FindBugs says that TimedEvent "defined 086 * compareTo(Object) and uses Object.equals()" 087 * http://findbugs.sourceforge.net/bugDescriptions.html#EQ_COMPARETO_USE_OBJECT_EQUALS 088 * says: "This class defines a compareTo(...) method but 089 * inherits its equals() method from 090 * java.lang.Object. Generally, the value of compareTo should 091 * return zero if and only if equals returns true. If this is 092 * violated, weird and unpredictable failures will occur in 093 * classes such as PriorityQueue. In Java 5 the 094 * PriorityQueue.remove method uses the compareTo method, 095 * while in Java 6 it uses the equals method. 096 * 097 * From the JavaDoc for the compareTo method in the 098 * Comparable interface: 099 * 100 * It is strongly recommended, but not strictly required that 101 * (x.compareTo(y)==0) == (x.equals(y)). Generally speaking, 102 * any class that implements the Comparable interface and 103 * violates this condition should clearly indicate this 104 * fact. The recommended language is "Note: this class has a 105 * natural ordering that is inconsistent with equals." " 106 */ 107 if (timedEvent == this) { 108 return true; 109 } 110 if (timedEvent == null || timedEvent.getClass() != getClass()) { 111 return false; 112 } else { 113 TimedEvent event = (TimedEvent) timedEvent; 114 if (compareTo(event) == 0 && contents.equals(event.contents)) { 115 return true; 116 } 117 } 118 return false; 119 } 120 121 /** Compare two TimedEvents by comparing their timestamps. 122 * @param timedEvent The event to compare against. 123 * @return The integer -1, 0, or 1 if this is less than, equal to, or 124 * greater than the argument. 125 */ 126 @Override 127 public int compareTo(TimedEvent timedEvent) { 128 return timeStamp.compareTo(timedEvent.timeStamp); 129 } 130 131 /** Return the hash code for the TimedEvent object. If two 132 * TimedEvent objects contains the same timestamp, 133 * and event object, then they will have the same hashCode. 134 * @return The hash code for this TimedEvent object. 135 */ 136 @Override 137 public int hashCode() { 138 int hashCode = 21; 139 if (timeStamp != null) { 140 hashCode = 31 * hashCode + timeStamp.hashCode(); 141 } 142 if (contents != null) { 143 hashCode = 31 * hashCode + contents.hashCode(); 144 } 145 return hashCode; 146 } 147 148 /////////////////////////////////////////////////////////////////// 149 //// inner classes //// 150 151 /////////////////////////////////////////////////////////////////// 152 //// TimeComparator 153 154 /** 155 * This class implements the CQComparator interface. It compares instances 156 * of TimedEvent. Therefore, all arguments passed to its methods have 157 * to be of type TimedEvent (or TimedEvent[] for the getBinWidth() method). 158 * If this is violated, ClassCastException will be thrown. 159 */ 160 public static class TimeComparator implements CQComparator { 161 /** Construct a TimeComparator object. 162 */ 163 public TimeComparator() { 164 _binWidth = 1; 165 _zeroReference = 0.0; 166 } 167 168 /////////////////////////////////////////////////////////////////// 169 //// public methods //// 170 171 /** Compare the two arguments. Return a negative integer, 172 * zero, or a positive integer depending on whether 173 * the first argument is less than, 174 * equal to, or greater than the second. 175 * Both arguments have to be instances of TimedEvent, otherwise a 176 * ClassCastException will be thrown. 177 * @param object1 The first event. 178 * @param object2 The second event. 179 * @return -1, 0, or +1 depending on whether the first 180 * argument is less than, equal to, or greater than the second. 181 * @exception ClassCastException If either argument is not an instance 182 * of TimedEvent. 183 */ 184 @Override 185 public int compare(Object object1, Object object2) { 186 TimedEvent a = (TimedEvent) object1; 187 TimedEvent b = (TimedEvent) object2; 188 return a.timeStamp.compareTo(b.timeStamp); 189 } 190 191 /** Given an entry, return a virtual bin number for the entry. 192 * The calculation performed is: 193 * <p> 194 * <i>(entry.timeStamp - zeroReference) / binWidth</i>, 195 * </p> 196 * with the result cast to long. 197 * If the arguments are not instances of TimedEvent, then a 198 * ClassCastException will be thrown. 199 * If the bin number is larger than what can be represented 200 * in a long, then the low-order 64 bits will be returned. 201 * Note that this could change the sign of the result, but 202 * the way this is used in the CalendarQueue class, this is OK. 203 * It is converted to a bin number by masking some number of 204 * low-order bits, so the result will be unaffected by the 205 * sign error. 206 * @param entry The entry. 207 * @return The virtual bin number for the entry, according to the 208 * current zero reference and the bin width. 209 * @exception ClassCastException If the arguments are not instances of 210 * TimedEvent. 211 */ 212 @Override 213 public long getVirtualBinNumber(Object entry) { 214 // Note: The longValue() method will only 215 // returns the low-order 64 bits of the result. 216 // If it is larger than what can be represented 217 // in 64 bits, then the returned result will be wrapped. 218 long value = (long) (((TimedEvent) entry).timeStamp 219 .subtract(_zeroReference).getLongValue() / _binWidth); 220 if (value != Long.MAX_VALUE) { 221 return value; 222 } else { 223 return Long.MAX_VALUE - 1; 224 } 225 } 226 227 /** Given an array of TimedEvent objects, find the appropriate bin 228 * width. By 'appropriate', we mean that 229 * the bin width is chosen such that on average 230 * the number of entries in all non-empty bins is equal to one. 231 * If the argument is null, return the default bin width, 232 * which is 1.0 for this implementation. 233 * Otherwise, the statistics of the elements of the array 234 * are analyzed to determine a reasonable bin width. 235 * 236 * @param entryArray An array of TimedEvent objects. 237 * @exception ClassCastException If one of the array elements is not 238 * an instance of TimedEvent. 239 */ 240 @Override 241 public void setBinWidth(Object[] entryArray) { 242 if (entryArray == null) { 243 // Reset to default. 244 _binWidth = 1; 245 _zeroReference = 0.0; 246 return; 247 } 248 249 double[] diff = new double[entryArray.length - 1]; 250 251 Time firstEntryTime = ((TimedEvent) entryArray[0]).timeStamp; 252 Time lastEntryTime = ((TimedEvent) entryArray[entryArray.length 253 - 1]).timeStamp; 254 255 if (firstEntryTime.isInfinite() 256 && firstEntryTime.equals(lastEntryTime)) { 257 // To avoid setting NaN or 0.0 258 // for the width, apparently due to simultaneous events, 259 // we leave it unchanged instead. 260 return; 261 } 262 263 double average = lastEntryTime.subtract(firstEntryTime) 264 .getDoubleValue(); 265 average = average / (entryArray.length - 1); 266 267 double effAverage = 0.0; 268 int nEffSamples = 0; 269 270 for (int i = 1; i < entryArray.length; ++i) { 271 if (diff[i - 1] < 2 * average) { 272 nEffSamples++; 273 effAverage = effAverage + diff[i - 1]; 274 } 275 } 276 277 // To avoid returning NaN or 0.0 for the width, if this is 278 // the result, leave the bin width unchanged. 279 if (effAverage == 0 || nEffSamples == 0) { 280 return; 281 } 282 283 effAverage = effAverage / nEffSamples; 284 _binWidth = effAverage * 3; 285 } 286 287 /** Set the zero reference, to be used in calculating the virtual 288 * bin number. 289 * @exception ClassCastException If the argument is not an instance 290 * of TimedEvent. 291 */ 292 @Override 293 public void setZeroReference(Object zeroReference) { 294 _zeroReference = ((TimedEvent) zeroReference).timeStamp 295 .getDoubleValue(); 296 } 297 298 /////////////////////////////////////////////////////////////////// 299 //// private members //// 300 301 // The bin width. 302 private double _binWidth; 303 304 // The zero reference. 305 private double _zeroReference; 306 } 307 308}