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