001package jmri.jmrit.operations.automation; 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.rollingstock.cars.CarManagerXml; 016import jmri.jmrit.operations.rollingstock.engines.EngineManagerXml; 017import jmri.jmrit.operations.setup.Control; 018import jmri.jmrit.operations.setup.OperationsSetupXml; 019import jmri.jmrit.operations.trains.TrainManagerXml; 020 021/** 022 * Manages automations. 023 * 024 * @author Bob Jacobsen Copyright (C) 2003 025 * @author Daniel Boudreau Copyright (C) 2016 026 */ 027public class AutomationManager extends PropertyChangeSupport implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize, PropertyChangeListener { 028 029 public static final String LISTLENGTH_CHANGED_PROPERTY = "automationListLength"; // NOI18N 030 private int _id = 0; // retain highest automation Id seen to ensure no Id 031 // collisions 032 033 public AutomationManager() { 034 } 035 036 // stores known Automation instances by id 037 protected Hashtable<String, Automation> _automationHashTable = new Hashtable<>(); 038 039 protected Automation _startupAutomation; 040 041 /** 042 * @return Number of automations 043 */ 044 public int getSize() { 045 return _automationHashTable.size(); 046 } 047 048 /** 049 * @param name The string name of the automation to be returned. 050 * @return requested Automation object or null if none exists 051 */ 052 public Automation getAutomationByName(String name) { 053 Automation automation; 054 Enumeration<Automation> en = _automationHashTable.elements(); 055 while (en.hasMoreElements()) { 056 automation = en.nextElement(); 057 if (automation.getName().equals(name)) { 058 return automation; 059 } 060 } 061 return null; 062 } 063 064 public Automation getAutomationById(String id) { 065 return _automationHashTable.get(id); 066 } 067 068 /** 069 * Finds an existing automation or creates a new automation if needed 070 * requires automation's name creates a unique id for this automation 071 * 072 * @param name The string name of the automation. 073 * @return new automation or existing automation 074 */ 075 public Automation newAutomation(String name) { 076 Automation automation = getAutomationByName(name); 077 if (automation == null) { 078 _id++; 079 automation = new Automation(Integer.toString(_id), name); 080 Integer oldSize = Integer.valueOf(_automationHashTable.size()); 081 _automationHashTable.put(automation.getId(), automation); 082 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, Integer.valueOf(_automationHashTable 083 .size())); 084 } 085 return automation; 086 } 087 088 /** 089 * Remember a NamedBean Object created outside the manager. 090 * 091 * @param automation The automation that is being registered. 092 */ 093 public void register(Automation automation) { 094 Integer oldSize = Integer.valueOf(_automationHashTable.size()); 095 _automationHashTable.put(automation.getId(), automation); 096 // find last id created 097 int id = Integer.parseInt(automation.getId()); 098 if (id > _id) { 099 _id = id; 100 } 101 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, 102 Integer.valueOf(_automationHashTable.size())); 103 } 104 105 /** 106 * Forget a NamedBean Object created outside the manager. 107 * 108 * @param automation The automation to be deleted. 109 */ 110 public void deregister(Automation automation) { 111 if (automation == null) { 112 return; 113 } 114 automation.dispose(); 115 Integer oldSize = Integer.valueOf(_automationHashTable.size()); 116 _automationHashTable.remove(automation.getId()); 117 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, 118 Integer.valueOf(_automationHashTable.size())); 119 } 120 121 /** 122 * Sort by automation name 123 * 124 * @return list of automations ordered by name 125 */ 126 public List<Automation> getAutomationsByNameList() { 127 List<Automation> sortList = getList(); 128 // now re-sort 129 List<Automation> out = new ArrayList<>(); 130 for (Automation automation : sortList) { 131 for (int j = 0; j < out.size(); j++) { 132 if (automation.getName().compareToIgnoreCase(out.get(j).getName()) < 0) { 133 out.add(j, automation); 134 break; 135 } 136 } 137 if (!out.contains(automation)) { 138 out.add(automation); 139 } 140 } 141 return out; 142 143 } 144 145 /** 146 * Sort by automation id number 147 * 148 * @return list of automations ordered by id number 149 */ 150 public List<Automation> getAutomationsByIdList() { 151 List<Automation> sortList = getList(); 152 // now re-sort 153 List<Automation> out = new ArrayList<>(); 154 for (Automation automation : sortList) { 155 for (int j = 0; j < out.size(); j++) { 156 try { 157 if (Integer.parseInt(automation.getId()) < Integer.parseInt(out.get(j).getId())) { 158 out.add(j, automation); 159 break; 160 } 161 } catch (NumberFormatException e) { 162 log.debug("list id number isn't a number"); 163 } 164 } 165 if (!out.contains(automation)) { 166 out.add(automation); 167 } 168 } 169 return out; 170 } 171 172 private List<Automation> getList() { 173 List<Automation> out = new ArrayList<>(); 174 Enumeration<Automation> en = _automationHashTable.elements(); 175 while (en.hasMoreElements()) { 176 out.add(en.nextElement()); 177 } 178 return out; 179 } 180 181 /** 182 * Gets a JComboBox loaded with automations. 183 * 184 * @return JComboBox with a list of automations. 185 */ 186 public JComboBox<Automation> getComboBox() { 187 JComboBox<Automation> box = new JComboBox<>(); 188 updateComboBox(box); 189 return box; 190 } 191 192 /** 193 * Update a JComboBox with the latest automations. 194 * 195 * @param box the JComboBox needing an update. 196 */ 197 public void updateComboBox(JComboBox<Automation> box) { 198 box.removeAllItems(); 199 box.addItem(null); 200 for (Automation automation : getAutomationsByNameList()) { 201 box.addItem(automation); 202 } 203 } 204 205 /** 206 * Restarts all automations that were running when the operations program 207 * was last saved. 208 */ 209 public void resumeAutomations() { 210 for (Automation automation : getAutomationsByNameList()) { 211 if (!automation.isActionRunning() && !automation.isReadyToRun()) { 212 automation.resume(); 213 } 214 } 215 } 216 217 /** 218 * Makes a new copy of automation 219 * 220 * @param automation the automation to copy 221 * @param newName name for the copy of automation 222 * @return new copy of automation 223 */ 224 public Automation copyAutomation(Automation automation, String newName) { 225 Automation newAutomation = newAutomation(newName); 226 newAutomation.copyAutomation(automation); 227 return newAutomation; 228 } 229 230 public Automation getStartupAutomation() { 231 return _startupAutomation; 232 } 233 234 protected String getStartupAutomationId() { 235 String id = ""; 236 if (getStartupAutomation() != null) { 237 id = getStartupAutomation().getId(); 238 } 239 return id; 240 } 241 242 public void setStartupAutomation(Automation automation) { 243 Automation old = _startupAutomation; 244 _startupAutomation = automation; 245 setDirtyAndFirePropertyChange("automationStartupIdChanged", old, automation); 246 } 247 248 public void runStartupAutomation() { 249 Automation startup = getStartupAutomation(); 250 if (startup != null) { 251 log.debug("Run automation: {}", startup.getName()); 252 startup.run(); 253 } 254 } 255 256 public void dispose() { 257 _automationHashTable.clear(); 258 _id = 0; 259 } 260 261 /** 262 * Construct this Entry from XML. This member has to remain synchronized 263 * with the detailed DTD in operations-trains.dtd 264 * 265 * @param root Consist XML element 266 */ 267 public void load(Element root) { 268 if (root.getChild(Xml.AUTOMATIONS) != null) { 269 List<Element> eAutomations = root.getChild(Xml.AUTOMATIONS).getChildren(Xml.AUTOMATION); 270 log.debug("readFile sees {} automations", eAutomations.size()); 271 for (Element eAutomation : eAutomations) { 272 register(new Automation(eAutomation)); 273 } 274 } 275 // get startup automation after all of the automations have been loaded 276 Element e = root.getChild(Xml.AUTOMATION_OPTIONS); 277 Attribute a; 278 if (e != null) { 279 if ((a = e.getAttribute(Xml.AUTOMATION_STARTUP_ID)) != null) { 280 _startupAutomation = getAutomationById(a.getValue()); 281 } 282 } 283 } 284 285 /** 286 * Create an XML element to represent this Entry. This member has to remain 287 * synchronized with the detailed DTD in operations-trains.dtd. 288 * 289 * @param root Contents in a JDOM Element 290 */ 291 public void store(Element root) { 292 Element e = new Element(Xml.AUTOMATION_OPTIONS); 293 e.setAttribute(Xml.AUTOMATION_STARTUP_ID, getStartupAutomationId()); 294 root.addContent(e); 295 Element values; 296 root.addContent(values = new Element(Xml.AUTOMATIONS)); 297 // add entries 298 for (Automation automation : getAutomationsByNameList()) { 299 values.addContent(automation.store()); 300 } 301 } 302 303 @Override 304 public void propertyChange(java.beans.PropertyChangeEvent e) { 305 if (Control.SHOW_PROPERTY) { 306 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e 307 .getNewValue()); 308 } 309 } 310 311 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 312 // set dirty 313 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 314 firePropertyChange(p, old, n); 315 } 316 317 @Override 318 public void initialize() { 319 InstanceManager.getDefault(OperationsSetupXml.class); // load setup 320 InstanceManager.getDefault(CarManagerXml.class); // load cars 321 InstanceManager.getDefault(EngineManagerXml.class); // load engines 322 InstanceManager.getDefault(TrainManagerXml.class); // load trains 323 } 324 325 private final static Logger log = LoggerFactory.getLogger(AutomationManager.class); 326 327}