001package jmri.jmrit.operations.routes; 002 003import java.util.ArrayList; 004import java.util.Enumeration; 005import java.util.Hashtable; 006import java.util.List; 007 008import javax.swing.JComboBox; 009 010import org.jdom2.Element; 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014import jmri.InstanceManager; 015import jmri.InstanceManagerAutoDefault; 016import jmri.InstanceManagerAutoInitialize; 017import jmri.beans.PropertyChangeSupport; 018import jmri.jmrit.operations.locations.Location; 019import jmri.jmrit.operations.locations.LocationManager; 020import jmri.jmrit.operations.setup.OperationsSetupXml; 021 022/** 023 * Manages the routes 024 * 025 * @author Bob Jacobsen Copyright (C) 2003 026 * @author Daniel Boudreau Copyright (C) 2008, 2009, 2010 027 */ 028public class RouteManager extends PropertyChangeSupport implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize { 029 030 public static final String LISTLENGTH_CHANGED_PROPERTY = "routesListLengthChanged"; // NOI18N 031 032 public RouteManager() { 033 } 034 035 private int _id = 0; 036 037 public void dispose() { 038 _routeHashTable.clear(); 039 _id = 0; 040 } 041 042 // stores known Route instances by id 043 protected Hashtable<String, Route> _routeHashTable = new Hashtable<>(); 044 045 /** 046 * @param name The string name of the Route. 047 * @return requested Route object or null if none exists 048 */ 049 public Route getRouteByName(String name) { 050 Route l; 051 Enumeration<Route> en = _routeHashTable.elements(); 052 while (en.hasMoreElements()) { 053 l = en.nextElement(); 054 if (l.getName().equals(name)) { 055 return l; 056 } 057 } 058 return null; 059 } 060 061 public Route getRouteById(String id) { 062 return _routeHashTable.get(id); 063 } 064 065 /** 066 * Finds an existing route or creates a new route if needed requires route's 067 * name creates a unique id for this route 068 * 069 * @param name The string name of the new Route. 070 * 071 * 072 * @return new route or existing route 073 */ 074 public Route newRoute(String name) { 075 Route route = getRouteByName(name); 076 if (route == null) { 077 _id++; 078 route = new Route(Integer.toString(_id), name); 079 Integer oldSize = Integer.valueOf(_routeHashTable.size()); 080 _routeHashTable.put(route.getId(), route); 081 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, 082 Integer.valueOf(_routeHashTable.size())); 083 } 084 return route; 085 } 086 087 /** 088 * Remember a NamedBean Object created outside the manager. 089 * 090 * @param route The Route to add. 091 */ 092 public void register(Route route) { 093 Integer oldSize = Integer.valueOf(_routeHashTable.size()); 094 _routeHashTable.put(route.getId(), route); 095 // find last id created 096 int id = Integer.parseInt(route.getId()); 097 if (id > _id) { 098 _id = id; 099 } 100 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, Integer.valueOf(_routeHashTable.size())); 101 // listen for name and state changes to forward 102 } 103 104 /** 105 * Forget a NamedBean Object created outside the manager. 106 * 107 * @param route The Route to delete. 108 */ 109 public void deregister(Route route) { 110 if (route == null) { 111 return; 112 } 113 route.dispose(); 114 Integer oldSize = Integer.valueOf(_routeHashTable.size()); 115 _routeHashTable.remove(route.getId()); 116 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, Integer.valueOf(_routeHashTable.size())); 117 } 118 119 /** 120 * Sort by route name 121 * 122 * @return list of routes ordered by name 123 */ 124 public List<Route> getRoutesByNameList() { 125 List<Route> sortList = getList(); 126 // now re-sort 127 List<Route> out = new ArrayList<>(); 128 for (Route route : sortList) { 129 for (int j = 0; j < out.size(); j++) { 130 if (route.getName().compareToIgnoreCase(out.get(j).getName()) < 0) { 131 out.add(j, route); 132 break; 133 } 134 } 135 if (!out.contains(route)) { 136 out.add(route); 137 } 138 } 139 return out; 140 141 } 142 143 /** 144 * Sort by route number, number can alpha numeric 145 * 146 * @return list of routes ordered by id numbers 147 */ 148 public List<Route> getRoutesByIdList() { 149 List<Route> sortList = getList(); 150 // now re-sort 151 List<Route> out = new ArrayList<>(); 152 for (Route route : sortList) { 153 for (int j = 0; j < out.size(); j++) { 154 try { 155 if (Integer.parseInt(route.getId()) < Integer.parseInt(out.get(j).getId())) { 156 out.add(j, route); 157 break; 158 } 159 } catch (NumberFormatException e) { 160 log.error("list id number isn't a number"); 161 } 162 } 163 if (!out.contains(route)) { 164 out.add(route); 165 } 166 } 167 return out; 168 } 169 170 private List<Route> getList() { 171 List<Route> out = new ArrayList<>(); 172 Enumeration<Route> en = _routeHashTable.elements(); 173 while (en.hasMoreElements()) { 174 out.add(en.nextElement()); 175 } 176 return out; 177 } 178 179 public JComboBox<Route> getComboBox() { 180 JComboBox<Route> box = new JComboBox<>(); 181 box.addItem(null); 182 List<Route> routes = getRoutesByNameList(); 183 for (Route route : routes) { 184 box.addItem(route); 185 } 186 return box; 187 } 188 189 public void updateComboBox(JComboBox<Route> box) { 190 box.removeAllItems(); 191 box.addItem(null); 192 List<Route> routes = getRoutesByNameList(); 193 for (Route route : routes) { 194 box.addItem(route); 195 } 196 } 197 198 /** 199 * Copy route, returns a new route named routeName. If invert is true the 200 * reverse of the route is returned. 201 * 202 * @param route The route to be copied 203 * @param routeName The name of the new route 204 * @param invert If true, return the inversion of route 205 * @return A copy of the route 206 */ 207 public Route copyRoute(Route route, String routeName, boolean invert) { 208 Route newRoute = newRoute(routeName); 209 List<RouteLocation> routeList = route.getLocationsBySequenceList(); 210 if (!invert) { 211 for (RouteLocation rl : routeList) { 212 copyRouteLocation(newRoute, rl, null, invert); 213 } 214 // invert route order 215 } else { 216 for (int i = routeList.size() - 1; i >= 0; i--) { 217 int y = i - 1; 218 if (y < 0) { 219 y = 0; 220 } 221 copyRouteLocation(newRoute, routeList.get(i), routeList.get(y), invert); 222 } 223 } 224 newRoute.setComment(route.getComment()); 225 return newRoute; 226 } 227 228 private void copyRouteLocation(Route newRoute, RouteLocation rl, RouteLocation rlNext, boolean invert) { 229 Location loc = InstanceManager.getDefault(LocationManager.class).getLocationByName(rl.getName()); 230 RouteLocation rlNew = newRoute.addLocation(loc); 231 // now copy the route location objects we want 232 rlNew.setMaxCarMoves(rl.getMaxCarMoves()); 233 rlNew.setRandomControl(rl.getRandomControl()); 234 rlNew.setWait(rl.getWait()); 235 rlNew.setDepartureTime(rl.getDepartureTime()); 236 rlNew.setComment(rl.getComment()); 237 rlNew.setCommentColor(rl.getCommentColor()); 238 if (!invert) { 239 rlNew.setDropAllowed(rl.isDropAllowed()); 240 rlNew.setPickUpAllowed(rl.isPickUpAllowed()); 241 rlNew.setGrade(rl.getGrade()); 242 rlNew.setTrainDirection(rl.getTrainDirection()); 243 rlNew.setMaxTrainLength(rl.getMaxTrainLength()); 244 } else { 245 // flip set outs and pick ups 246 rlNew.setDropAllowed(rl.isPickUpAllowed()); 247 rlNew.setPickUpAllowed(rl.isDropAllowed()); 248 // invert train directions 249 int oldDirection = rl.getTrainDirection(); 250 if (oldDirection == RouteLocation.NORTH) { 251 rlNew.setTrainDirection(RouteLocation.SOUTH); 252 } else if (oldDirection == RouteLocation.SOUTH) { 253 rlNew.setTrainDirection(RouteLocation.NORTH); 254 } else if (oldDirection == RouteLocation.EAST) { 255 rlNew.setTrainDirection(RouteLocation.WEST); 256 } else if (oldDirection == RouteLocation.WEST) { 257 rlNew.setTrainDirection(RouteLocation.EAST); 258 } 259 // get the max length between location 260 if (rlNext == null) { 261 log.error("Can not copy route, rlNext is null!"); 262 return; 263 } 264 rlNew.setMaxTrainLength(rlNext.getMaxTrainLength()); 265 } 266 rlNew.setTrainIconX(rl.getTrainIconX()); 267 rlNew.setTrainIconY(rl.getTrainIconY()); 268 } 269 270 /** 271 * @return Number of routes 272 */ 273 public int numEntries() { 274 return _routeHashTable.size(); 275 } 276 277 public void load(Element root) { 278 // decode type, invoke proper processing routine if a decoder file 279 if (root.getChild(Xml.ROUTES) != null) { 280 List<Element> eRoutes = root.getChild(Xml.ROUTES).getChildren(Xml.ROUTE); 281 log.debug("readFile sees {} routes", eRoutes.size()); 282 for (Element eRoute : eRoutes) { 283 register(new Route(eRoute)); 284 } 285 } 286 } 287 288 public void store(Element root) { 289 Element values = new Element(Xml.ROUTES); 290 root.addContent(values); 291 for (Route route : getRoutesByIdList()) { 292 values.addContent(route.store()); 293 } 294 } 295 296 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 297 InstanceManager.getDefault(RouteManagerXml.class).setDirty(true); 298 firePropertyChange(p, old, n); 299 } 300 301 private final static Logger log = LoggerFactory.getLogger(RouteManager.class); 302 303 @Override 304 public void initialize() { 305 InstanceManager.getDefault(OperationsSetupXml.class); // load setup 306 InstanceManager.getDefault(RouteManagerXml.class); // load routes 307 } 308 309}