001package jmri.jmrit.timetable; 002 003import java.util.ArrayList; 004import java.util.List; 005import java.util.Collections; 006import java.util.TreeMap; 007 008/** 009 * Provide data base management services. 010 * <p> 011 * The data structure was migrated from a MySQL database. As such, it contains 012 * <strong>tables</strong> implemented as TreeMaps and <strong>records</strong> 013 * implemented as Classes. The logical relationships are handled using foreign keys. 014 * 015 * <pre> 016 * Data Structure: 017 * Layout -- Global data. 018 * TrainTypes -- Assigned to trains for diagram colors. 019 * Segments -- Used for division / sub-division arrangements. 020 * Stations -- Any place a train can stop. 021 * Schedules -- Basic information about a schedule. 022 * Trains -- Train characteristics. 023 * Stops -- A junction between a train and a station that contains arrival and departure times. 024 * </pre> 025 * 026 * @author Dave Sand Copyright (C) 2018 027 */ 028public class TimeTableDataManager { 029 030 /** 031 * Create a TimeTableDataManager instance. 032 * @param loadData False to create an empty instance, otherwise load the data 033 */ 034 public TimeTableDataManager(boolean loadData) { 035 jmri.InstanceManager.setDefault(TimeTableDataManager.class, this); 036 if (loadData) { 037 _lockCalculate = true; 038 if (!jmri.jmrit.timetable.configurexml.TimeTableXml.doLoad()) { 039 log.error("Unable to load the time table data"); // NOI18N 040 } 041 _lockCalculate = false; 042 } 043 } 044 045 /** 046 * Use the InstanceManager to only allow a single data manager instance. 047 * @return the current or new data manager. 048 */ 049 public static TimeTableDataManager getDataManager() { 050 TimeTableDataManager dm = jmri.InstanceManager.getNullableDefault(TimeTableDataManager.class); 051 if (dm != null) { 052 return dm; 053 } 054 return new TimeTableDataManager(true); 055 } 056 057 // Exception key words 058 public static final String CLOCK_LT_1 = "FastClockLt1"; // NOI18N 059 public static final String DURATION_LT_0 = "DurationLt0"; // NOI18N 060 public static final String THROTTLES_LT_0 = "ThrottlesLt0"; // NOI18N 061 public static final String THROTTLES_IN_USE = "ThrottlesInUse"; // NOI18N 062 public static final String SCALE_NF = "ScaleNotFound"; // NOI18N 063 public static final String TIME_OUT_OF_RANGE = "TimeOutOfRange"; // NOI18N 064 public static final String SEGMENT_CHANGE_ERROR = "SegmentChangeError"; // NOI18N 065 public static final String DISTANCE_LT_0 = "DistanceLt0"; // NOI18N 066 public static final String SIDINGS_LT_0 = "SidingsLt0"; // NOI18N 067 public static final String STAGING_LT_0 = "StagingLt0"; // NOI18N 068 public static final String STAGING_IN_USE = "StagingInUse"; // NOI18N 069 public static final String START_HOUR_RANGE = "StartHourRange"; // NOI18N 070 public static final String DURATION_RANGE = "DurationRange"; // NOI18N 071 public static final String DEFAULT_SPEED_LT_0 = "DefaultSpeedLt0"; // NOI18N 072 public static final String START_TIME_FORMAT = "StartTimeFormat"; // NOI18N 073 public static final String START_TIME_RANGE = "StartTimeRange"; // NOI18N 074 public static final String THROTTLE_RANGE = "ThrottleRange"; // NOI18N 075 public static final String STAGING_RANGE = "StagingRange"; // NOI18N 076 public static final String STOP_DURATION_LT_0 = "StopDurationLt0"; // NOI18N 077 public static final String NEXT_SPEED_LT_0 = "NextSpeedLt0"; // NOI18N 078 public static final String LAYOUT_HAS_CHILDREN = "LayoutHasChildren"; // NOI18N 079 public static final String TYPE_HAS_REFERENCE = "TypeHasReference"; // NOI18N 080 public static final String SEGMENT_HAS_CHILDREN = "SegmentHaSChildren"; // NOI18N 081 public static final String STATION_HAS_REFERENCE = "StationHasReference"; // NOI18N 082 public static final String SCHEDULE_HAS_CHILDREN = "ScheduleHasChildren"; // NOI18N 083 public static final String TRAIN_HAS_CHILDREN = "TrainHasChildren"; // NOI18N 084 public static final String TYPE_ADD_FAIL = "TypeAddFail"; // NOI18N 085 public static final String SEGMENT_ADD_FAIL = "SegmentAddFail"; // NOI18N 086 public static final String STATION_ADD_FAIL = "StationAddFail"; // NOI18N 087 public static final String SCHEDULE_ADD_FAIL = "ScheduleAddFail"; // NOI18N 088 public static final String TRAIN_ADD_FAIL = "TrainAddFail"; // NOI18N 089 public static final String STOP_ADD_FAIL = "StopAddFail"; // NOI18N 090 091 private TreeMap<Integer, Layout> _layoutMap = new TreeMap<>(); 092 private TreeMap<Integer, TrainType> _trainTypeMap = new TreeMap<>(); 093 private TreeMap<Integer, Segment> _segmentMap = new TreeMap<>(); 094 private TreeMap<Integer, Station> _stationMap = new TreeMap<>(); 095 private TreeMap<Integer, Schedule> _scheduleMap = new TreeMap<>(); 096 private TreeMap<Integer, Train> _trainMap = new TreeMap<>(); 097 private TreeMap<Integer, Stop> _stopMap = new TreeMap<>(); 098 099 private List<SegmentStation> _segmentStations = new ArrayList<>(); 100 101 boolean _lockCalculate = false; 102 public void setLockCalculate(boolean lock) { 103 _lockCalculate = lock; 104 } 105 106 // ------------ Map maintenance methods ------------ // 107 108 public void addLayout(int id, Layout newLayout) { 109 _layoutMap.put(id, newLayout); 110 } 111 112 public void addTrainType(int id, TrainType newTrainType) { 113 _trainTypeMap.put(id, newTrainType); 114 } 115 116 public void addSegment(int id, Segment newSegment) { 117 _segmentMap.put(id, newSegment); 118 } 119 120 /** 121 * Add a new station. 122 * Create a SegmentStation instance. 123 * Add it to the SegmentStation list. 124 * @param id map id. 125 * @param newStation the new station. 126 */ 127 public void addStation(int id, Station newStation) { 128 _stationMap.put(id, newStation); 129 SegmentStation segmentStation = new SegmentStation(newStation.getSegmentId(), id); 130 if (!_segmentStations.contains(segmentStation)) { 131 _segmentStations.add(segmentStation); 132 } 133 } 134 135 public void addSchedule(int id, Schedule newSchedule) { 136 _scheduleMap.put(id, newSchedule); 137 } 138 139 public void addTrain(int id, Train newTrain) { 140 _trainMap.put(id, newTrain); 141 } 142 143 public void addStop(int id, Stop newStop) { 144 _stopMap.put(id, newStop); 145 } 146 147 /** 148 * Delete the layout if there are no train types, segments or schedules. 149 * @param id The layout id. 150 * @throws IllegalArgumentException LAYOUT_HAS_CHILDREN 151 */ 152 public void deleteLayout(int id) { 153 if (!getTrainTypes(id, false).isEmpty() 154 || !getSegments(id, false).isEmpty() 155 || !getSchedules(id, false).isEmpty()) { 156 throw new IllegalArgumentException(LAYOUT_HAS_CHILDREN); 157 } 158 _layoutMap.remove(id); 159 } 160 161 /** 162 * Delete the train type if there are no train references. 163 * @param id The train type id. 164 * @throws IllegalArgumentException TYPE_HAS_REFERENCE 165 */ 166 public void deleteTrainType(int id) { 167 if (!getTrains(0, id, false).isEmpty()) { 168 throw new IllegalArgumentException(TYPE_HAS_REFERENCE); 169 } 170 _trainTypeMap.remove(id); 171 } 172 173 /** 174 * Delete the segment if it has no stations. 175 * @param id The segment id. 176 * @throws IllegalArgumentException SEGMENT_HAS_CHILDREN 177 */ 178 public void deleteSegment(int id) { 179 if (!getStations(id, false).isEmpty()) { 180 throw new IllegalArgumentException(SEGMENT_HAS_CHILDREN); 181 } 182 _segmentMap.remove(id); 183 } 184 185 /** 186 * Delete the station if there are no stop references. 187 * @param id The station id. 188 * @throws IllegalArgumentException STATION_HAS_REFERENCE 189 */ 190 public void deleteStation(int id) { 191 if (!getStops(0, id, false).isEmpty()) { 192 throw new IllegalArgumentException(STATION_HAS_REFERENCE); 193 } 194 195 int segmentId = getStation(id).getSegmentId(); 196 List<SegmentStation> list = new ArrayList<>(); 197 for (SegmentStation segmentStation : _segmentStations) { 198 if (segmentStation.getStationId() == id && segmentStation.getSegmentId() == segmentId) { 199 list.add(segmentStation); 200 } 201 } 202 for (SegmentStation ss : list) { 203 _segmentStations.remove(ss); 204 } 205 206 _stationMap.remove(id); 207 } 208 209 /** 210 * Delete the schedule if it has no trains. 211 * @param id The schedule id. 212 * @throws IllegalArgumentException SCHEDULE_HAS_CHILDREN 213 */ 214 public void deleteSchedule(int id) { 215 if (!getTrains(id, 0, false).isEmpty()) { 216 throw new IllegalArgumentException(SCHEDULE_HAS_CHILDREN); 217 } 218 _scheduleMap.remove(id); 219 } 220 221 /** 222 * Delete the train if it has no stops. 223 * @param id The train id. 224 * @throws IllegalArgumentException TRAIN_HAS_CHILDREN 225 */ 226 public void deleteTrain(int id) { 227 if (!getStops(id, 0, false).isEmpty()) { 228 throw new IllegalArgumentException(TRAIN_HAS_CHILDREN); 229 } 230 _trainMap.remove(id); 231 } 232 233 /** 234 * Delete the stop and update train schedule. 235 * @param id The stop id. 236 */ 237 public void deleteStop(int id) { 238 int trainId = getStop(id).getTrainId(); 239 _stopMap.remove(id); 240 calculateTrain(trainId, true); 241 } 242 243 // ------------ Map access methods: get by id ------------ // 244 245 public Layout getLayout(int id) { 246 return _layoutMap.get(id); 247 } 248 249 public TrainType getTrainType(int id) { 250 return _trainTypeMap.get(id); 251 } 252 253 public Segment getSegment(int id) { 254 return _segmentMap.get(id); 255 } 256 257 public Station getStation(int id) { 258 return _stationMap.get(id); 259 } 260 261 public Schedule getSchedule(int id) { 262 return _scheduleMap.get(id); 263 } 264 265 public Train getTrain(int id) { 266 return _trainMap.get(id); 267 } 268 269 public Stop getStop(int id) { 270 return _stopMap.get(id); 271 } 272 273 /** 274 * Get the last key from the map and add 1. 275 * @param type The record type which is used to select the appropriate map. 276 * @return the next id, or 0 if there is an error. 277 */ 278 public int getNextId(String type) { 279 int nextId = 0; 280 switch (type) { 281 case "Layout": // NOI18N 282 nextId = (_layoutMap.isEmpty()) ? 1 : _layoutMap.lastKey() + 1; 283 break; 284 case "TrainType": // NOI18N 285 nextId = (_trainTypeMap.isEmpty()) ? 1 : _trainTypeMap.lastKey() + 1; 286 break; 287 case "Segment": // NOI18N 288 nextId = (_segmentMap.isEmpty()) ? 1 : _segmentMap.lastKey() + 1; 289 break; 290 case "Station": // NOI18N 291 nextId = (_stationMap.isEmpty()) ? 1 : _stationMap.lastKey() + 1; 292 break; 293 case "Schedule": // NOI18N 294 nextId = (_scheduleMap.isEmpty()) ? 1 : _scheduleMap.lastKey() + 1; 295 break; 296 case "Train": // NOI18N 297 nextId = (_trainMap.isEmpty()) ? 1 : _trainMap.lastKey() + 1; 298 break; 299 case "Stop": // NOI18N 300 nextId = (_stopMap.isEmpty()) ? 1 : _stopMap.lastKey() + 1; 301 break; 302 default: 303 log.error("getNextId: Invalid record type: {}", type); // NOI18N 304 } 305 return nextId; 306 } 307 308 // ------------ Map access methods: get all entries or by foreign key ------------ // 309 310 /** 311 * Create a list of layouts 312 * @param sort If true, sort the resulting list 313 * @return a list of layouts 314 */ 315 public List<Layout> getLayouts(boolean sort) { 316 // No foreign keys 317 ArrayList<Layout> list = new ArrayList<>(_layoutMap.values()); 318 if (sort) { 319 Collections.sort(list, (o1, o2) -> o1.getLayoutName().compareTo(o2.getLayoutName())); 320 } 321 return list; 322 } 323 324 /** 325 * Create a list of train types 326 * @param fKeyLayout If non-zero, select the types that have the specified foreign key 327 * @param sort If true, sort the resulting list 328 * @return a list of train types 329 */ 330 public List<TrainType> getTrainTypes(int fKeyLayout, boolean sort) { 331 ArrayList<TrainType> list = new ArrayList<>(); 332 for (TrainType type : _trainTypeMap.values()) { 333 if (fKeyLayout == 0 || fKeyLayout == type.getLayoutId()) { 334 list.add(type); 335 } 336 } 337 if (sort) { 338 Collections.sort(list, (o1, o2) -> o1.getTypeName().compareTo(o2.getTypeName())); 339 } 340 return list; 341 } 342 343 /** 344 * Create a list of segments 345 * @param fKeyLayout If non-zero, select the segments that have the specified foreign key 346 * @param sort If true, sort the resulting list 347 * @return a list of segments 348 */ 349 public List<Segment> getSegments(int fKeyLayout, boolean sort) { 350 ArrayList<Segment> list = new ArrayList<>(); 351 for (Segment segment : _segmentMap.values()) { 352 if (fKeyLayout == 0 || fKeyLayout == segment.getLayoutId()) { 353 list.add(segment); 354 } 355 } 356 if (sort) { 357 Collections.sort(list, (o1, o2) -> o1.getSegmentName().compareTo(o2.getSegmentName())); 358 } 359 return list; 360 } 361 362 /** 363 * Create a list of stations 364 * @param fKeySegment If non-zero, select the stations that have the specified foreign key 365 * @param sort If true, sort the resulting list 366 * @return a list of stations 367 */ 368 public List<Station> getStations(int fKeySegment, boolean sort) { 369 ArrayList<Station> list = new ArrayList<>(); 370 for (Station station : _stationMap.values()) { 371 if (fKeySegment == 0 || fKeySegment == station.getSegmentId()) { 372 list.add(station); 373 } 374 } 375 if (sort) { 376 Collections.sort(list, (o1, o2) -> Double.compare(o1.getDistance(), o2.getDistance())); 377 } 378 return list; 379 } 380 381 /** 382 * Create a list of schedules 383 * @param fKeyLayout If non-zero, select the schedules that have the specified foreign key 384 * @param sort If true, sort the resulting list 385 * @return a list of schedules 386 */ 387 public List<Schedule> getSchedules(int fKeyLayout, boolean sort) { 388 ArrayList<Schedule> list = new ArrayList<>(); 389 for (Schedule schedule : _scheduleMap.values()) { 390 if (fKeyLayout == 0 || fKeyLayout == schedule.getLayoutId()) { 391 list.add(schedule); 392 } 393 } 394 if (sort) { 395 Collections.sort(list, (o1, o2) -> o1.getScheduleName().compareTo(o2.getScheduleName())); 396 } 397 return list; 398 } 399 400 /** 401 * Create a list of trains 402 * @param fKeySchedule If non-zero, select the trains that have the specified foreign key 403 * @param fKeyType If non-zero, select the trains that have the specified foreign key 404 * @param sort If true, sort the resulting list 405 * @return a list of trains 406 */ 407 public List<Train> getTrains(int fKeySchedule, int fKeyType, boolean sort) { 408 ArrayList<Train> list = new ArrayList<>(); 409 for (Train train : _trainMap.values()) { 410 if ((fKeySchedule == 0 && fKeyType == 0) 411 || fKeySchedule == train.getScheduleId() 412 || fKeyType == train.getTypeId()) { 413 list.add(train); 414 } 415 } 416 if (sort) { 417 Collections.sort(list, (o1, o2) -> o1.getTrainName().compareTo(o2.getTrainName())); 418 } 419 return list; 420 } 421 422 /** 423 * Create a list of stops 424 * @param fKeyTrain If non-zero, select the stops that have the specified foreign key 425 * @param fKeyStation If non-zero, select the stops that have the specified foreign key 426 * @param sort If true, sort the resulting list 427 * @return a list of stops 428 */ 429 public List<Stop> getStops(int fKeyTrain, int fKeyStation, boolean sort) { 430 ArrayList<Stop> list = new ArrayList<>(); 431 for (Stop stop : _stopMap.values()) { 432 if ((fKeyTrain == 0 && fKeyStation == 0) 433 || fKeyTrain == stop.getTrainId() 434 || fKeyStation == stop.getStationId()) { 435 list.add(stop); 436 } 437 } 438 if (sort) { 439 Collections.sort(list, (o1, o2) -> Integer.compare(o1.getSeq(), o2.getSeq())); 440 } 441 return list; 442 } 443 444 // ------------ Special Map access methods ------------ // 445 446 public Layout getLayoutForStop(int stopId) { 447 return getLayout(getSchedule(getTrain(getStop(stopId).getTrainId()).getScheduleId()).getLayoutId()); 448 } 449 450 public List<SegmentStation> getSegmentStations(int layoutId) { 451 List<SegmentStation> list = new ArrayList<>(); 452 for (SegmentStation segmentStation : _segmentStations) { 453 if (getSegment(segmentStation.getSegmentId()).getLayoutId() == layoutId) { 454 list.add(segmentStation); 455 } 456 } 457 Collections.sort(list, (o1, o2) -> o1.toString().compareTo(o2.toString())); 458 return list; 459 } 460 461 // ------------ Calculate Train Times ------------ 462 463 /** 464 * Update the stops for all of the trains for this layout. 465 * Invoked by updates to fast clock speed, metric, scale and station distances. 466 * @param layoutId The id for the layout that has been updated. 467 * @param updateStops True for update 468 */ 469 void calculateLayoutTrains(int layoutId, boolean updateStops) { 470 if (_lockCalculate) return; 471 for (Schedule schedule : getSchedules(layoutId, false)) { 472 calculateScheduleTrains(schedule.getScheduleId(), updateStops); 473 } 474 } 475 476 /** 477 * Update the stop times for all of the trains that use this schedule. 478 * @param scheduleId The id for the schedule that has been updated. 479 * @param updateStops True for update 480 */ 481 void calculateScheduleTrains(int scheduleId, boolean updateStops) { 482 if (_lockCalculate) return; 483 for (Train train : getTrains(scheduleId, 0, false)) { 484 calculateTrain(train.getTrainId(), updateStops); 485 } 486 } 487 488 /** 489 * Calculate the arrival and departure times for all of the stops. 490 * @param trainId The id of the train to be updated. 491 * @param updateStops When true, update the arrive and depart times. 492 * @throws IllegalArgumentException when stop times are outside of the 493 * schedule times or a segment change failed. The TIME_OUT_OF_RANGE 494 * exception message includes the stop id and train name. The SEGMENT_CHANGE_ERROR 495 * message includes the segment name and the station name. The tilde 496 * character is used as the string separator. 497 */ 498 public void calculateTrain(int trainId, boolean updateStops) { 499 if (_lockCalculate) return; 500 Train train = getTrain(trainId); 501 Schedule schedule = getSchedule(train.getScheduleId()); 502 Layout layout = getLayout(schedule.getLayoutId()); 503 List<Stop> stops = getStops(trainId, 0, true); 504 505 double smile = layout.getScaleMK(); 506 int startHH = schedule.getStartHour(); 507 int duration = schedule.getDuration(); 508 int currentTime = train.getStartTime(); 509 int defaultSpeed = train.getDefaultSpeed(); 510 511 int checkStart = startHH; 512 int checkDuration = duration; 513 514 String currentStationName = ""; 515 double currentDistance = 0.0; 516 int currentSegment = 0; 517 int currentSpeed = 0; 518 int newArrive = 0; 519 int newDepart = 0; 520 int elapseTime = 0; 521 boolean firstStop = true; 522 523 int routeStart = currentTime; 524 int routeEnd = 0; 525 526 for (Stop stop : stops) { 527 Station station = getStation(stop.getStationId()); 528 Segment segment = getSegment(station.getSegmentId()); 529 if (firstStop) { 530 newArrive = currentTime; 531 currentTime += stop.getDuration(); 532 newDepart = currentTime; 533 currentDistance = station.getDistance(); 534 currentSpeed = (stop.getNextSpeed() > 0) ? stop.getNextSpeed() : defaultSpeed; 535 currentStationName = station.getStationName(); 536 currentSegment = segment.getSegmentId(); 537 538 if (validateTime(checkStart, checkDuration, newArrive) && validateTime(checkStart, checkDuration, newDepart)) { 539 if (updateStops) { 540 stop.setArriveTime(newArrive); 541 stop.setDepartTime(newDepart); 542 } 543 } else { 544 throw new IllegalArgumentException(String.format("%s~%d~%s", TIME_OUT_OF_RANGE, stop.getSeq(), train.getTrainName())); // NOI18N 545 } 546 firstStop = false; 547 continue; 548 } 549 550 // Calculate times for remaining stops 551 double wrkDistance = Math.abs(currentDistance - station.getDistance()); 552 553 // If the segment has changed, a new distance will need to be calculated. 554 if (segment.getSegmentId() != currentSegment) { 555 // Find the station in the current segment that has the same name 556 // as the station in the previous segment. 557 Station wrkStation = null; 558 for (Station findStation : getStations(segment.getSegmentId(), false)) { 559 if (findStation.getStationName().equals(currentStationName)) { 560 wrkStation = findStation; 561 break; 562 } 563 } 564 if (wrkStation == null) { 565 throw new IllegalArgumentException(SEGMENT_CHANGE_ERROR); 566 } 567 wrkDistance = Math.abs(station.getDistance() - wrkStation.getDistance()); 568 } 569 570 elapseTime = (int) Math.round(wrkDistance / smile / currentSpeed * 60); 571 if (elapseTime < 1) { 572 elapseTime = 1; 573 } 574 currentTime += elapseTime; 575 if (currentTime > 1439) 576 currentTime -= 1440; 577 newArrive = currentTime; 578 currentTime += stop.getDuration(); 579 if (currentTime > 1439) 580 currentTime -= 1440; 581 newDepart = currentTime; 582 routeEnd = currentTime; 583 584 currentDistance = station.getDistance(); 585 currentSpeed = (stop.getNextSpeed() > 0) ? stop.getNextSpeed() : defaultSpeed; 586 currentSegment = station.getSegmentId(); 587 currentStationName = station.getStationName(); 588 589 if (validateTime(checkStart, checkDuration, newArrive) && validateTime(checkStart, checkDuration, newDepart)) { 590 if (updateStops) { 591 stop.setArriveTime(newArrive); 592 stop.setDepartTime(newDepart); 593 } 594 } else { 595 throw new IllegalArgumentException(String.format("%s~%d~%s", TIME_OUT_OF_RANGE, stop.getSeq(), train.getTrainName())); // NOI18N 596 } 597 } 598 if (updateStops) { 599// log.info("train {} route start {}, end {}", train.getTrainName(), routeStart, routeEnd); 600 var routeDuration = routeEnd - routeStart; 601 // Adjust for day wrap 602 if (routeEnd < routeStart) { 603 routeDuration = (1440 - routeStart) + routeEnd; 604 } 605 train.setRouteDuration(routeDuration); 606 } 607 } 608 609 /** 610 * Check to see if the supplied time is within the time range for the supplied schedule. 611 * If the duration is 24 hours, then all times are valid. 612 * Otherwise, we need to calculate the valid range, which can span midnight. 613 * @param checkStart The schedule start hour. 614 * @param checkDuration The schedule duration. 615 * @param checkTime The time value to be check. 616 * @return true if the time is valid. 617 */ 618 public boolean validateTime(int checkStart, int checkDuration, int checkTime) { 619 if (checkDuration == 24 && checkTime < 1440) { 620 return true; 621 } 622 623 boolean dayWrap; 624 int lowLimit; 625 int highLimit; 626 627 if (checkStart + checkDuration > 24) { 628 dayWrap = true; 629 lowLimit = checkStart * 60; 630 highLimit = ((checkStart + checkDuration - 24) * 60) - 1; 631 } else { 632 dayWrap = false; 633 lowLimit = checkStart * 60; 634 highLimit = ((checkStart + checkDuration) * 60) - 1; 635 } 636 637 if (dayWrap) { 638 if (checkTime < 1440 && (checkTime >= lowLimit || checkTime <= highLimit)) { 639 return true; 640 } 641 } else { 642 if (checkTime < 1440 && (checkTime >= lowLimit && checkTime <= highLimit)) { 643 return true; 644 } 645 } 646 return false; 647 } 648 649 /** 650 * Internal class that provides a combined segment and station view. 651 */ 652 public class SegmentStation { 653 private int _segmentId; 654 private int _stationId; 655 656 public SegmentStation(int segmentId, int stationId) { 657 _segmentId = segmentId; 658 _stationId = stationId; 659 } 660 661 public int getSegmentId() { 662 return _segmentId; 663 } 664 665 public int getStationId() { 666 return _stationId; 667 } 668 669 @Override 670 public String toString() { 671 return String.format("%s : %s", getSegment(_segmentId).getSegmentName(), getStation(_stationId).getStationName()); // NOI18N 672 } 673 } 674 675 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TimeTableDataManager.class); 676}