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