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