001 package edu.nrao.sss.util; 002 003 import java.util.EnumSet; 004 005 /** 006 * An indicator for where a collection of events is in its life cycle. 007 * <p> 008 * This enumeration is appropriate for the status of an event that 009 * is really a collection of finer grained events. For the most part, 010 * the statuses of an atomic event and an event that is a collection 011 * of atomic events, overlap. The one place they differ is in their 012 * treatments of "in progress" and "under way".</p> 013 * <p> 014 * See also {@link EventStatus}.</p> 015 * <p> 016 * <b>Version Info:</b> 017 * <table style="margin-left:2em"> 018 * <tr><td>$Revision: 2184 $</td></tr> 019 * <tr><td>$Date: 2009-04-10 15:00:07 -0600 (Fri, 10 Apr 2009) $</td></tr> 020 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 021 * </table></p> 022 * 023 * @author David M. Harland 024 * @since 2007-08-15 025 */ 026 public enum EventSetStatus 027 { 028 /** 029 * Indicates that no event in a set is ready for scheduling. 030 */ 031 NOT_READY_TO_BE_SCHEDULED(false), 032 033 /** 034 * Indicates that no event in a set has been scheduled yet. 035 */ 036 NOT_YET_SCHEDULED(false), 037 038 /** 039 * Indicates that no event in a set has begun yet, but that at least 040 * one has been scheduled. 041 */ 042 SCHEDULED_BUT_NOT_STARTED(false), 043 044 /** 045 * Indicates that at least one event in a set is currently in progress, 046 * but says nothing about the other events in that set. 047 * If any one event in a set is in progress, then the set of events 048 * is also said to be in progress. 049 * <p> 050 * This status should be used only when an activity really is in progress. 051 * One place where this is ambiguous is as follows. Imagine that we 052 * have a container that has a collection of components. Each component 053 * has an execution status, as does the container. The execution status 054 * of the container, though, is derived from that of its components. 055 * Imagine that some of the components have been completed and that the 056 * rest of them have not yet begun. One could say that the container 057 * is "in progress", as it has begun but not yet been completed. 058 * However, we would really like to see this status used only when the 059 * activity is really happening. Had one of the above components been 060 * in progress and all others as stated above, then this status would be 061 * appropriate for the container. However, as given in the example, a 062 * better status would be {@link #UNDER_WAY}.</p> 063 */ 064 IN_PROGRESS(false), 065 066 /** 067 * Indicates that at least one, but not all, of the events in a set 068 * has reached the end of its life cycle. 069 * This status tells you that none of the component events 070 * are <tt>IN_PROGRESS</tt> or <tt>ON_HOLD</tt>. 071 */ 072 UNDER_WAY(false), 073 074 /** 075 * Indicates that a set of events has been completed. 076 * Note that it is possible that some of the elemental events 077 * in the set may have been canceled, rather than completed. 078 * All elemental events, though, have reached the ends of 079 * their life cycles. 080 */ 081 COMPLETED(true), 082 083 /** 084 * Indicates that at least one event in a set has been put on hold 085 * and that none are currently in progress. 086 * Unless one or more events in a set is <tt>IN_PROGRESS</tt>, if 087 * any event in a set is <tt>ON_HOLD</tt>, then the set itself is 088 * said to be on hold. 089 * <p> 090 * An actor must act to take an event out of this 091 * status. Note that this is subtly different than postponing 092 * an event, which places its status immediately back to 093 * <tt>NOT_YET_SCHEDULED</tt>.</p> 094 */ 095 ON_HOLD(false), 096 097 /** 098 * Indicates that an entire set of events has been canceled. 099 * A canceled event will not be resumed or restarted. 100 */ 101 CANCELED(true); 102 103 private boolean isFinal; 104 105 EventSetStatus(boolean isFinal) 106 { 107 this.isFinal = isFinal; 108 } 109 110 /** 111 * Returns <i>true</i> if this status is a final status for an event. 112 * <tt>CANCELED</tt> and <tt>COMPLETED</tt> are examples of final 113 * statuses. 114 * 115 * @return <i>true</i> if this status is a final status for an event. 116 */ 117 public boolean isFinal() 118 { 119 return isFinal; 120 } 121 122 /** 123 * Uses the {@code componentStatuses} to derive an <tt>EventSetStatus</tt>. 124 * This method is appropriate when your event holds a collection of events, 125 * but the events that your event holds do <i>not</i> themselves hold other 126 * events. 127 * <p> 128 * Some of the rules used by this method are as follows:</p> 129 * <ol> 130 * <li>If the list of component statuses is <i>null</i> or empty, then the 131 * returned status is NOT_READY_TO_BE_SCHEDULED.</li> 132 * <li>If any of the component statuses are IN_PROGRESS, then the 133 * returned status is IN_PROGRESS.</li> 134 * <li>If all the component statuses are the same, then that 135 * singular status type is returned.</li> 136 * <p><i>We reach the next items only if none of the above resulted 137 * in an answer.</i></p> 138 * <li>If some of the component statuses are final statuses and some are 139 * nonfinal, then: 140 * <ol type="a"> 141 * <li>if there is only a single type of non-final status, 142 * that type is returned;</li> 143 * <li>otherwise the returned status is UNDER_WAY.</li> 144 * </ol> 145 * </li> 146 * <li>If all are final, then: 147 * <ol type="a"> 148 * <li>if one or more component statuses are COMPLETE, then the 149 * returned status is COMPLETE;</li> 150 * <li>otherwise the returned status is CANCELED.</li> 151 * </ol> 152 * </li> 153 * <li>If all are nonfinal, then: 154 * <ol type="a"> 155 * <li>if one or more component statuses are 156 * SCHEDULED_BUT_NOT_STARTED, then the 157 * returned status is SCHEDULED_BUT_NOT_STARTED,</li> 158 * <li>otherwise the returned status is NOT_YET_SCHEDULED.</li> 159 * </ol> 160 * </li> 161 * </ol> 162 * 163 * @param componentStatuses a set of event statuses from which can be derived 164 * a single status for a set of events. 165 * 166 * @return an <tt>EventSetStatus</tt> that represents a logic combination of 167 * the <tt>componentStatuses</tt>. 168 */ 169 public static EventSetStatus createFrom(EventStatus... componentStatuses) 170 { 171 EventSetStatus answer = null; 172 173 if (componentStatuses == null || componentStatuses.length == 0) 174 { 175 answer = EventSetStatus.NOT_READY_TO_BE_SCHEDULED; 176 } 177 else if (componentStatuses.length == 1) 178 { 179 answer = componentStatuses[0].toEventSetStatus(); 180 } 181 else //length > 1 182 { 183 //Eliminates duplicates and lets us use the contains method 184 EnumSet<EventStatus> statuses = 185 EnumSet.of(componentStatuses[0], componentStatuses); 186 187 //If all the componentStatuses were identical return 188 if (statuses.size() == 1) 189 { 190 answer = statuses.iterator().next().toEventSetStatus(); 191 } 192 193 //From here down we know we have more than one kind of status 194 195 //A single in-progress trumps all else 196 else if (statuses.contains(EventStatus.IN_PROGRESS)) 197 { 198 answer = EventSetStatus.IN_PROGRESS; 199 } 200 //Need to look at combinations 201 else 202 { 203 EnumSet<EventStatus> finalStatuses = EnumSet.noneOf(EventStatus.class); 204 EnumSet<EventStatus> nonFinalStatuses = EnumSet.noneOf(EventStatus.class); 205 206 for (EventStatus eventStatus : statuses) 207 { 208 if (eventStatus.isFinal()) 209 finalStatuses.add(eventStatus); 210 else 211 nonFinalStatuses.add(eventStatus); 212 } 213 214 //Final + NonFinal = under way 215 //Final only = completed or canceled 216 if (!finalStatuses.isEmpty()) 217 { 218 if (!nonFinalStatuses.isEmpty()) 219 { 220 //If we have only ONE non-final status, return it instead of UNDER_WAY 221 answer = nonFinalStatuses.size() == 1 ? 222 nonFinalStatuses.iterator().next().toEventSetStatus() : 223 EventSetStatus.UNDER_WAY; 224 } 225 else //only final statuses 226 { 227 answer = finalStatuses.contains(EventStatus.COMPLETED) ? 228 EventSetStatus.COMPLETED : EventSetStatus.CANCELED; 229 } 230 } 231 //NonFinal only: need to look at the non-final types 232 else 233 { 234 //Remember that IN_PROGRESS was handled early on, so we can't have 235 //that status here. For ON_HOLD, we've decided we won't return 236 //that status unless it is the only non-final type, so we never 237 //return it here, even if it is present. 238 if (nonFinalStatuses.contains(EventStatus.SCHEDULED_BUT_NOT_STARTED)) 239 { 240 answer = EventSetStatus.SCHEDULED_BUT_NOT_STARTED; 241 } 242 else if (nonFinalStatuses.contains(EventStatus.NOT_YET_SCHEDULED)) 243 { 244 answer = EventSetStatus.NOT_YET_SCHEDULED; 245 } 246 else 247 { 248 throw new RuntimeException( 249 "Logic error: not able to handle this set of non-final statuses: " 250 + nonFinalStatuses); 251 } 252 } 253 } 254 } 255 256 return answer; 257 } 258 259 /** 260 * Uses the {@code componentStatuses} to derive a single 261 * <tt>EventSetStatus</tt>. This method is appropriate when your event 262 * holds a collection of events, each which also holds a collection of 263 * events. 264 * <p> 265 * Some of the rules used by this method are as follows:</p> 266 * <ol> 267 * <li>If the list of component statuses is <i>null</i> or empty, then the 268 * returned status is NOT_READY_TO_BE_SCHEDULED.</li> 269 * <li>If any of the component statuses are IN_PROGRESS, then the 270 * returned status is IN_PROGRESS.</li> 271 * <li>If all the component statuses are the same, then that 272 * singular status type is returned.</li> 273 * <p><i>We reach the next items only if none of the above resulted 274 * in an answer.</i></p> 275 * <li>If some of the component statuses are final statuses and some are 276 * nonfinal, then: 277 * <ol type="a"> 278 * <li>if there is only a single type of non-final status, 279 * that type is returned;</li> 280 * <li>otherwise the returned status is UNDER_WAY.</li> 281 * </ol> 282 * </li> 283 * <li>If all are final, then: 284 * <ol type="a"> 285 * <li>if one or more component statuses are COMPLETE, then the 286 * returned status is COMPLETE;</li> 287 * <li>otherwise the returned status is CANCELED.</li> 288 * </ol> 289 * </li> 290 * <li>If all are nonfinal, then: 291 * <ol type="a"> 292 * <li>if one or more component statuses are 293 * UNDER_WAY, then the 294 * returned status is UNDER_WAY,</li> 295 * <li>otherwise, if one or more component statuses are 296 * SCHEDULED_BUT_NOT_STARTED, then the 297 * returned status is SCHEDULED_BUT_NOT_STARTED,</li> 298 * <li>otherwise the returned status is NOT_YET_SCHEDULED.</li> 299 * </ol> 300 * </li> 301 * </ol> 302 * 303 * @param componentStatuses a set of event set statuses from which can be 304 * derived a single status for a set of events. 305 * 306 * @return an <tt>EventSetStatus</tt> that represents a logic combination of 307 * the <tt>componentStatuses</tt>. 308 */ 309 //The logic here is very similar, but not identical, to that of the 310 //other createFrom method. 311 public static EventSetStatus createFrom(EventSetStatus... componentStatuses) 312 { 313 EventSetStatus answer = null; 314 315 if (componentStatuses == null || componentStatuses.length == 0) 316 { 317 answer = EventSetStatus.NOT_READY_TO_BE_SCHEDULED; 318 } 319 else if (componentStatuses.length == 1) 320 { 321 answer = componentStatuses[0]; 322 } 323 else //length > 1 324 { 325 //Eliminates duplicates and lets us use the contains method 326 EnumSet<EventSetStatus> statuses = 327 EnumSet.of(componentStatuses[0], componentStatuses); 328 329 //If all the componentStatuses were identical return 330 if (statuses.size() == 1) 331 { 332 answer = statuses.iterator().next(); 333 } 334 335 //From here down we know we have more than one kind of status 336 337 //A single in-progress trumps all else 338 else if (statuses.contains(EventSetStatus.IN_PROGRESS)) 339 { 340 answer = EventSetStatus.IN_PROGRESS; 341 } 342 //Need to look at combinations 343 else 344 { 345 EnumSet<EventSetStatus> finalStatuses = 346 EnumSet.noneOf(EventSetStatus.class); 347 348 EnumSet<EventSetStatus> nonFinalStatuses = 349 EnumSet.noneOf(EventSetStatus.class); 350 351 for (EventSetStatus setStatus : componentStatuses) 352 { 353 if (setStatus.isFinal()) 354 finalStatuses.add(setStatus); 355 else 356 nonFinalStatuses.add(setStatus); 357 } 358 359 //Final + NonFinal = under way 360 //Final only = completed or canceled 361 if (!finalStatuses.isEmpty()) 362 { 363 if (!nonFinalStatuses.isEmpty()) 364 { 365 //If we have only ONE non-final status, return it instead of UNDER_WAY 366 answer = nonFinalStatuses.size() == 1 ? 367 nonFinalStatuses.iterator().next() : EventSetStatus.UNDER_WAY; 368 } 369 else //only final statuses 370 { 371 answer = finalStatuses.contains(EventSetStatus.COMPLETED) ? 372 EventSetStatus.COMPLETED : EventSetStatus.CANCELED; 373 } 374 } 375 //NonFinal only: need to look at the non-final types 376 else 377 { 378 //Remember that IN_PROGRESS was handled early on, so we can't have 379 //that status here. For ON_HOLD, we've decided we won't return 380 //that status unless it is the only non-final type, so we never 381 //return it here, even if it is present. 382 if (nonFinalStatuses.contains(EventSetStatus.UNDER_WAY)) 383 { 384 answer = EventSetStatus.UNDER_WAY; 385 } 386 else if (nonFinalStatuses.contains(EventSetStatus.SCHEDULED_BUT_NOT_STARTED)) 387 { 388 answer = EventSetStatus.SCHEDULED_BUT_NOT_STARTED; 389 } 390 else if (nonFinalStatuses.contains(EventSetStatus.NOT_YET_SCHEDULED)) 391 { 392 answer = EventSetStatus.NOT_YET_SCHEDULED; 393 } 394 else 395 { 396 throw new RuntimeException( 397 "Logic error: not able to handle this set of non-final statuses: " 398 + nonFinalStatuses); 399 } 400 } 401 } 402 } 403 404 return answer; 405 } 406 407 /** 408 * Returns a text representation of this enumeration constant. 409 * @return a text representation of this enumeration constant. 410 */ 411 public String toString() 412 { 413 return EnumerationUtility.getSharedInstance().enumToString(this); 414 } 415 416 /** 417 * Returns the status represented by {@code text}. 418 * <p> 419 * For details about the transformation, see 420 * {@link EnumerationUtility#enumFromString(Class, String)}.</p> 421 * 422 * @param text a text representation of a status. 423 * 424 * @return the status represented by {@code text}. 425 */ 426 public static EventSetStatus fromString(String text) 427 { 428 return EnumerationUtility.getSharedInstance() 429 .enumFromString(EventSetStatus.class, text); 430 } 431 }