001package jmri.jmrit.operations.trains.schedules; 002 003import java.beans.PropertyChangeListener; 004import java.util.*; 005 006import javax.swing.JComboBox; 007 008import org.jdom2.Attribute; 009import org.jdom2.Element; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013import jmri.*; 014import jmri.beans.PropertyChangeSupport; 015import jmri.jmrit.operations.locations.Location; 016import jmri.jmrit.operations.locations.LocationManager; 017import jmri.jmrit.operations.setup.Setup; 018import jmri.jmrit.operations.trains.*; 019import jmri.jmrit.operations.trains.csv.TrainCsvSwitchLists; 020 021/** 022 * Manages train schedules. The default is the days of the week, but can be 023 * anything the user wants when defining when trains will run. 024 * 025 * @author Bob Jacobsen Copyright (C) 2003 026 * @author Daniel Boudreau Copyright (C) 2010 027 */ 028public class TrainScheduleManager extends PropertyChangeSupport implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize, PropertyChangeListener { 029 030 public TrainScheduleManager() { 031 } 032 033 public static final String NONE = ""; 034 private String _trainScheduleActiveId = NONE; 035 private int _id = 0; 036 037 public static final String LISTLENGTH_CHANGED_PROPERTY = "trainScheduleListLength"; // NOI18N 038 public static final String SCHEDULE_ID_CHANGED_PROPERTY = "ActiveTrainScheduleId"; // NOI18N 039 040 public void dispose() { 041 _scheduleHashTable.clear(); 042 } 043 044 // stores known TrainSchedule instances by id 045 protected Hashtable<String, TrainSchedule> _scheduleHashTable = new Hashtable<>(); 046 047 /** 048 * @return Number of schedules 049 */ 050 public int numEntries() { 051 return _scheduleHashTable.size(); 052 } 053 054 /** 055 * Sets the selected schedule id 056 * 057 * @param id Selected schedule id 058 */ 059 public void setTrainScheduleActiveId(String id) { 060 String old = _trainScheduleActiveId; 061 _trainScheduleActiveId = id; 062 if (!old.equals(id)) { 063 setDirtyAndFirePropertyChange(SCHEDULE_ID_CHANGED_PROPERTY, old, id); 064 } 065 } 066 067 public String getTrainScheduleActiveId() { 068 return _trainScheduleActiveId; 069 } 070 071 public TrainSchedule getActiveSchedule() { 072 return getScheduleById(getTrainScheduleActiveId()); 073 } 074 075 /** 076 * @param name The schedule string name to search for. 077 * @return requested TrainSchedule object or null if none exists 078 */ 079 public TrainSchedule getScheduleByName(String name) { 080 TrainSchedule s; 081 Enumeration<TrainSchedule> en = _scheduleHashTable.elements(); 082 while (en.hasMoreElements()) { 083 s = en.nextElement(); 084 if (s.getName().equals(name)) { 085 return s; 086 } 087 } 088 return null; 089 } 090 091 public TrainSchedule getScheduleById(String id) { 092 return _scheduleHashTable.get(id); 093 } 094 095 /** 096 * Finds an existing schedule or creates a new schedule if needed requires 097 * schedule's name creates a unique id for this schedule 098 * 099 * @param name The string name of the schedule. 100 * 101 * 102 * @return new TrainSchedule or existing TrainSchedule 103 */ 104 public TrainSchedule newSchedule(String name) { 105 TrainSchedule schedule = getScheduleByName(name); 106 if (schedule == null) { 107 _id++; 108 schedule = new TrainSchedule(Integer.toString(_id), name); 109 Integer oldSize = Integer.valueOf(_scheduleHashTable.size()); 110 _scheduleHashTable.put(schedule.getId(), schedule); 111 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, 112 Integer.valueOf(_scheduleHashTable.size())); 113 } 114 return schedule; 115 } 116 117 /** 118 * Remember a NamedBean Object created outside the manager. 119 * 120 * @param schedule The TrainSchedule to add. 121 */ 122 public void register(TrainSchedule schedule) { 123 Integer oldSize = Integer.valueOf(_scheduleHashTable.size()); 124 _scheduleHashTable.put(schedule.getId(), schedule); 125 // find last id created 126 int id = Integer.parseInt(schedule.getId()); 127 if (id > _id) { 128 _id = id; 129 } 130 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, Integer.valueOf(_scheduleHashTable.size())); 131 } 132 133 /** 134 * Forget a NamedBean Object created outside the manager. 135 * 136 * @param schedule The TrainSchedule to delete. 137 */ 138 public void deregister(TrainSchedule schedule) { 139 if (schedule == null) { 140 return; 141 } 142 Integer oldSize = Integer.valueOf(_scheduleHashTable.size()); 143 _scheduleHashTable.remove(schedule.getId()); 144 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, Integer.valueOf(_scheduleHashTable.size())); 145 } 146 147 /** 148 * Sort by train schedule name 149 * 150 * @return list of train schedules ordered by name 151 */ 152 public List<TrainSchedule> getSchedulesByNameList() { 153 List<TrainSchedule> sortList = getList(); 154 // now re-sort 155 List<TrainSchedule> out = new ArrayList<>(); 156 for (int i = 0; i < sortList.size(); i++) { 157 for (int j = 0; j < out.size(); j++) { 158 if (sortList.get(i).getName().compareToIgnoreCase(out.get(j).getName()) < 0) { 159 out.add(j, sortList.get(i)); 160 break; 161 } 162 } 163 if (!out.contains(sortList.get(i))) { 164 out.add(sortList.get(i)); 165 } 166 } 167 return out; 168 } 169 170 /** 171 * Sort by train schedule id numbers 172 * 173 * @return list of train schedules ordered by id numbers 174 */ 175 public List<TrainSchedule> getSchedulesByIdList() { 176 List<TrainSchedule> sortList = getList(); 177 // now re-sort 178 List<TrainSchedule> out = new ArrayList<>(); 179 for (int i = 0; i < sortList.size(); i++) { 180 for (int j = 0; j < out.size(); j++) { 181 try { 182 if (Integer.parseInt(sortList.get(i).getId()) < Integer.parseInt(out.get(j).getId())) { 183 out.add(j, sortList.get(i)); 184 break; 185 } 186 } catch (NumberFormatException e) { 187 log.debug("list id number isn't a number"); 188 } 189 } 190 if (!out.contains(sortList.get(i))) { 191 out.add(sortList.get(i)); 192 } 193 } 194 return out; 195 } 196 197 private List<TrainSchedule> getList() { 198 // no schedules? then load defaults 199 if (numEntries() == 0) { 200 createDefaultSchedules(); 201 } 202 List<TrainSchedule> out = new ArrayList<>(); 203 Enumeration<TrainSchedule> en = _scheduleHashTable.elements(); 204 while (en.hasMoreElements()) { 205 out.add(en.nextElement()); 206 } 207 return out; 208 } 209 210 /** 211 * Gets a JComboBox loaded with schedules starting with null. 212 * 213 * @return JComboBox with a list of schedules. 214 */ 215 public JComboBox<TrainSchedule> getComboBox() { 216 JComboBox<TrainSchedule> box = new JComboBox<>(); 217 updateComboBox(box); 218 return box; 219 } 220 221 /** 222 * Gets a JComboBox loaded with schedules starting with null. 223 * 224 * @return JComboBox with a list of schedules starting with null. 225 */ 226 public JComboBox<TrainSchedule> getSelectComboBox() { 227 JComboBox<TrainSchedule> box = new JComboBox<>(); 228 box.addItem(null); 229 for (TrainSchedule sch : getSchedulesByIdList()) { 230 box.addItem(sch); 231 } 232 return box; 233 } 234 235 /** 236 * Update a JComboBox with the latest schedules. 237 * 238 * @param box the JComboBox needing an update. 239 * @throws IllegalArgumentException if box is null 240 */ 241 public void updateComboBox(JComboBox<TrainSchedule> box) { 242 if (box == null) { 243 throw new IllegalArgumentException("Attempt to update non-existant comboBox"); 244 } 245 box.removeAllItems(); 246 for (TrainSchedule sch : getSchedulesByNameList()) { 247 box.addItem(sch); 248 } 249 } 250 251 public void buildSwitchLists() { 252 TrainSwitchLists trainSwitchLists = new TrainSwitchLists(); 253 TrainCsvSwitchLists trainCsvSwitchLists = new TrainCsvSwitchLists(); 254 String locationName = ""; // only create switch lists once for locations with similar names 255 for (Location location : InstanceManager.getDefault(LocationManager.class).getLocationsByNameList()) { 256 if (location.isSwitchListEnabled() && !locationName.equals(location.getSplitName())) { 257 trainCsvSwitchLists.buildSwitchList(location); 258 trainSwitchLists.buildSwitchList(location); 259 locationName = location.getSplitName(); 260 // print switch lists for locations that have changes 261 if (Setup.isSwitchListRealTime() && location.getStatus().equals(Location.UPDATED)) { 262 trainSwitchLists.printSwitchList(location, InstanceManager.getDefault(TrainManager.class).isPrintPreviewEnabled()); 263 } 264 } 265 } 266 // set trains switch lists printed 267 InstanceManager.getDefault(TrainManager.class).setTrainsSwitchListStatus(Train.PRINTED); 268 } 269 270 /** 271 * Create an XML element to represent this Entry. This member has to remain 272 * synchronized with the detailed DTD in operations-trains.dtd. 273 * 274 * @param root The common Element for operations-trains.dtd. 275 * 276 */ 277 public void store(Element root) { 278 Element e = new Element(Xml.TRAIN_SCHEDULE_OPTIONS); 279 e.setAttribute(Xml.ACTIVE_ID, InstanceManager.getDefault(TrainScheduleManager.class).getTrainScheduleActiveId()); 280 root.addContent(e); 281 Element values = new Element(Xml.SCHEDULES); 282 // add entries 283 List<TrainSchedule> schedules = getSchedulesByIdList(); 284 for (TrainSchedule schedule : schedules) { 285 values.addContent(schedule.store()); 286 } 287 root.addContent(values); 288 } 289 290 public void load(Element root) { 291 Element e = root.getChild(Xml.TRAIN_SCHEDULE_OPTIONS); 292 Attribute a; 293 if (e != null) { 294 if ((a = e.getAttribute(Xml.ACTIVE_ID)) != null) { 295 InstanceManager.getDefault(TrainScheduleManager.class).setTrainScheduleActiveId(a.getValue()); 296 } 297 } 298 299 e = root.getChild(Xml.SCHEDULES); 300 if (e != null) { 301 List<Element> eSchedules = root.getChild(Xml.SCHEDULES).getChildren(Xml.SCHEDULE); 302 log.debug("TrainScheduleManager sees {} train schedules", eSchedules.size()); 303 for (Element eSchedule : eSchedules) { 304 register(new TrainSchedule(eSchedule)); 305 } 306 } 307 } 308 309 public void createDefaultSchedules() { 310 log.debug("creating default schedules"); 311 newSchedule(Bundle.getMessage("Sunday")); 312 newSchedule(Bundle.getMessage("Monday")); 313 newSchedule(Bundle.getMessage("Tuesday")); 314 newSchedule(Bundle.getMessage("Wednesday")); 315 newSchedule(Bundle.getMessage("Thursday")); 316 newSchedule(Bundle.getMessage("Friday")); 317 newSchedule(Bundle.getMessage("Saturday")); 318 } 319 320 @Override 321 public void propertyChange(java.beans.PropertyChangeEvent e) { 322 log.debug("ScheduleManager sees property change: ({}) old: ({}) new ({})", 323 e.getPropertyName(), e.getOldValue(), e.getNewValue()); 324 } 325 326 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 327 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 328 firePropertyChange(p, old, n); 329 } 330 331 private final static Logger log = LoggerFactory.getLogger(TrainScheduleManager.class); 332 333 @Override 334 public void initialize() { 335 InstanceManager.getDefault(TrainManagerXml.class); // load trains 336 } 337 338}