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