001package jmri.jmrit.operations.locations.schedules; 002 003import java.beans.PropertyChangeListener; 004import java.util.*; 005 006import javax.swing.JComboBox; 007 008import org.jdom2.Element; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012import jmri.*; 013import jmri.beans.PropertyChangeSupport; 014import jmri.jmrit.operations.OperationsPanel; 015import jmri.jmrit.operations.locations.*; 016import jmri.jmrit.operations.rollingstock.cars.CarRoads; 017import jmri.jmrit.operations.rollingstock.cars.CarTypes; 018import jmri.jmrit.operations.setup.Control; 019 020/** 021 * Manages schedules. 022 * 023 * @author Bob Jacobsen Copyright (C) 2003 024 * @author Daniel Boudreau Copyright (C) 2008, 2013 025 */ 026public class ScheduleManager extends PropertyChangeSupport implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize, PropertyChangeListener { 027 028 public static final String LISTLENGTH_CHANGED_PROPERTY = "scheduleListLength"; // NOI18N 029 030 public ScheduleManager() { 031 } 032 033 private int _id = 0; 034 035 public void dispose() { 036 _scheduleHashTable.clear(); 037 } 038 039 // stores known Schedule instances by id 040 protected Hashtable<String, Schedule> _scheduleHashTable = new Hashtable<String, Schedule>(); 041 042 /** 043 * @return Number of schedules 044 */ 045 public int numEntries() { 046 return _scheduleHashTable.size(); 047 } 048 049 /** 050 * @param name The string name for the schedule 051 * @return requested Schedule object or null if none exists 052 */ 053 public Schedule getScheduleByName(String name) { 054 Schedule s; 055 Enumeration<Schedule> en = _scheduleHashTable.elements(); 056 while (en.hasMoreElements()) { 057 s = en.nextElement(); 058 if (s.getName().equals(name)) { 059 return s; 060 } 061 } 062 return null; 063 } 064 065 public Schedule getScheduleById(String id) { 066 return _scheduleHashTable.get(id); 067 } 068 069 /** 070 * Finds an existing schedule or creates a new schedule if needed requires 071 * schedule's name creates a unique id for this schedule 072 * 073 * @param name The string name for this schedule 074 * 075 * 076 * @return new schedule or existing schedule 077 */ 078 public Schedule newSchedule(String name) { 079 Schedule schedule = getScheduleByName(name); 080 if (schedule == null && !name.isBlank()) { 081 _id++; 082 schedule = new Schedule(Integer.toString(_id), name); 083 Integer oldSize = Integer.valueOf(_scheduleHashTable.size()); 084 _scheduleHashTable.put(schedule.getId(), schedule); 085 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, Integer.valueOf(_scheduleHashTable 086 .size())); 087 } 088 return schedule; 089 } 090 091 /** 092 * Remember a NamedBean Object created outside the manager. 093 * 094 * @param schedule The Schedule to add. 095 */ 096 public void register(Schedule schedule) { 097 Integer oldSize = Integer.valueOf(_scheduleHashTable.size()); 098 _scheduleHashTable.put(schedule.getId(), schedule); 099 // find last id created 100 int id = Integer.parseInt(schedule.getId()); 101 if (id > _id) { 102 _id = id; 103 } 104 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, Integer.valueOf(_scheduleHashTable.size())); 105 } 106 107 /** 108 * Forget a NamedBean Object created outside the manager. 109 * 110 * @param schedule The Schedule to delete. 111 */ 112 public void deregister(Schedule schedule) { 113 if (schedule == null) { 114 return; 115 } 116 schedule.dispose(); 117 Integer oldSize = Integer.valueOf(_scheduleHashTable.size()); 118 _scheduleHashTable.remove(schedule.getId()); 119 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, Integer.valueOf(_scheduleHashTable.size())); 120 } 121 122 /** 123 * Sort by schedule name 124 * 125 * @return list of schedules ordered by name 126 */ 127 public List<Schedule> getSchedulesByNameList() { 128 List<Schedule> sortList = getList(); 129 // now re-sort 130 List<Schedule> out = new ArrayList<Schedule>(); 131 for (Schedule sch : sortList) { 132 for (int j = 0; j < out.size(); j++) { 133 if (sch.getName().compareToIgnoreCase(out.get(j).getName()) < 0) { 134 out.add(j, sch); 135 break; 136 } 137 } 138 if (!out.contains(sch)) { 139 out.add(sch); 140 } 141 } 142 return out; 143 144 } 145 146 /** 147 * Sort by schedule id number 148 * 149 * @return list of schedules ordered by id number 150 */ 151 public List<Schedule> getSchedulesByIdList() { 152 List<Schedule> sortList = getList(); 153 // now re-sort 154 List<Schedule> out = new ArrayList<Schedule>(); 155 for (Schedule sch : sortList) { 156 for (int j = 0; j < out.size(); j++) { 157 try { 158 if (Integer.parseInt(sch.getId()) < Integer.parseInt(out.get(j).getId())) { 159 out.add(j, sch); 160 break; 161 } 162 } catch (NumberFormatException e) { 163 log.debug("list id number isn't a number"); 164 } 165 } 166 if (!out.contains(sch)) { 167 out.add(sch); 168 } 169 } 170 return out; 171 } 172 173 private List<Schedule> getList() { 174 List<Schedule> out = new ArrayList<Schedule>(); 175 Enumeration<Schedule> en = _scheduleHashTable.elements(); 176 while (en.hasMoreElements()) { 177 out.add(en.nextElement()); 178 } 179 return out; 180 } 181 182 public Schedule copySchedule(Schedule schedule, String newScheduleName) { 183 Schedule newSchedule = newSchedule(newScheduleName); 184 for (ScheduleItem si : schedule.getItemsBySequenceList()) { 185 ScheduleItem newSi = newSchedule.addItem(si.getTypeName()); 186 newSi.copyScheduleItem(si); 187 } 188 return newSchedule; 189 } 190 191 public void resetHitCounts() { 192 for (Schedule schedule : getList()) { 193 schedule.resetHitCounts(); 194 } 195 } 196 197 /** 198 * Gets a JComboBox loaded with schedules. 199 * 200 * @return JComboBox with a list of schedules. 201 */ 202 public JComboBox<Schedule> getComboBox() { 203 JComboBox<Schedule> box = new JComboBox<>(); 204 OperationsPanel.padComboBox(box, Control.max_len_string_location_name); 205 updateComboBox(box); 206 return box; 207 } 208 209 /** 210 * Update a JComboBox with the latest schedules. 211 * 212 * @param box the JComboBox needing an update. 213 */ 214 public void updateComboBox(JComboBox<Schedule> box) { 215 box.removeAllItems(); 216 box.addItem(null); 217 for (Schedule schedule : getSchedulesByNameList()) { 218 box.addItem(schedule); 219 } 220 } 221 222 /** 223 * Replaces car type in all schedules. 224 * 225 * @param oldType car type to be replaced. 226 * @param newType replacement car type. 227 */ 228 public void replaceType(String oldType, String newType) { 229 for (Schedule sch : getSchedulesByIdList()) { 230 for (ScheduleItem si : sch.getItemsBySequenceList()) { 231 if (si.getTypeName().equals(oldType)) { 232 si.setTypeName(newType); 233 } 234 } 235 } 236 } 237 238 /** 239 * Replaces car roads in all schedules. 240 * 241 * @param oldRoad car road to be replaced. 242 * @param newRoad replacement car road. 243 */ 244 public void replaceRoad(String oldRoad, String newRoad) { 245 if (newRoad == null) { 246 return; 247 } 248 for (Schedule sch : getSchedulesByIdList()) { 249 for (ScheduleItem si : sch.getItemsBySequenceList()) { 250 if (si.getRoadName().equals(oldRoad)) { 251 si.setRoadName(newRoad); 252 } 253 } 254 } 255 } 256 257 /** 258 * Replaces car loads in all schedules with specific car type. 259 * 260 * @param type car type. 261 * @param oldLoad car load to be replaced. 262 * @param newLoad replacement car load. 263 */ 264 public void replaceLoad(String type, String oldLoad, String newLoad) { 265 for (Schedule sch : getSchedulesByIdList()) { 266 for (ScheduleItem si : sch.getItemsBySequenceList()) { 267 if (si.getTypeName().equals(type) && si.getReceiveLoadName().equals(oldLoad)) { 268 if (newLoad != null) { 269 si.setReceiveLoadName(newLoad); 270 } else { 271 si.setReceiveLoadName(ScheduleItem.NONE); 272 } 273 } 274 if (si.getTypeName().equals(type) && si.getShipLoadName().equals(oldLoad)) { 275 if (newLoad != null) { 276 si.setShipLoadName(newLoad); 277 } else { 278 si.setShipLoadName(ScheduleItem.NONE); 279 } 280 } 281 } 282 } 283 } 284 285 public void replaceTrack(Track oldTrack, Track newTrack) { 286 for (Schedule sch : getSchedulesByIdList()) { 287 for (ScheduleItem si : sch.getItemsBySequenceList()) { 288 if (si.getDestinationTrack() == oldTrack) { 289 si.setDestination(newTrack.getLocation()); 290 si.setDestinationTrack(newTrack); 291 } 292 } 293 } 294 } 295 296 /** 297 * Gets a JComboBox with a list of spurs that use this schedule. 298 * 299 * @param schedule The schedule for this JComboBox. 300 * @return JComboBox with a list of spurs using schedule. 301 */ 302 public JComboBox<LocationTrackPair> getSpursByScheduleComboBox(Schedule schedule) { 303 JComboBox<LocationTrackPair> box = new JComboBox<>(); 304 // search all spurs for that use schedule 305 for (Track spur : getListSpurs(schedule)) { 306 if (spur.getScheduleId().equals(schedule.getId())) { 307 LocationTrackPair ltp = new LocationTrackPair(spur); 308 box.addItem(ltp); 309 } 310 } 311 return box; 312 } 313 314 public List<Track> getListSpurs(Schedule schedule) { 315 List<Track> spurs = new ArrayList<Track>(); 316 for (Location location : InstanceManager.getDefault(LocationManager.class).getLocationsByNameList()) { 317 for (Track spur : location.getTracksByNameList(Track.SPUR)) { 318 if (spur.getScheduleId().equals(schedule.getId())) { 319 spurs.add(spur); 320 } 321 } 322 } 323 return spurs; 324 } 325 326 public void load(Element root) { 327 if (root.getChild(Xml.SCHEDULES) != null) { 328 List<Element> eSchedules = root.getChild(Xml.SCHEDULES).getChildren(Xml.SCHEDULE); 329 log.debug("readFile sees {} schedules", eSchedules.size()); 330 for (Element eSchedule : eSchedules) { 331 register(new Schedule(eSchedule)); 332 } 333 } 334 } 335 336 public void store(Element root) { 337 Element values; 338 root.addContent(values = new Element(Xml.SCHEDULES)); 339 // add entries 340 for (Schedule schedule : getSchedulesByIdList()) { 341 values.addContent(schedule.store()); 342 } 343 } 344 345 /** 346 * Check for car type and road name changes. 347 */ 348 @Override 349 public void propertyChange(java.beans.PropertyChangeEvent e) { 350 if (Control.SHOW_PROPERTY) { 351 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e 352 .getNewValue()); 353 } 354 if (e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY)) { 355 replaceType((String) e.getOldValue(), (String) e.getNewValue()); 356 } 357 if (e.getPropertyName().equals(CarRoads.CARROADS_NAME_CHANGED_PROPERTY)) { 358 replaceRoad((String) e.getOldValue(), (String) e.getNewValue()); 359 } 360 } 361 362 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 363 // set dirty 364 InstanceManager.getDefault(LocationManagerXml.class).setDirty(true); 365 firePropertyChange(p, old, n); 366 } 367 368 private final static Logger log = LoggerFactory.getLogger(ScheduleManager.class); 369 370 @Override 371 public void initialize() { 372 InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this); 373 InstanceManager.getDefault(CarRoads.class).addPropertyChangeListener(this); 374 } 375 376}