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 * 068 * 069 * @return new route or existing route 070 */ 071 public Route newRoute(String name) { 072 Route route = getRouteByName(name); 073 if (route == null) { 074 _id++; 075 route = new Route(Integer.toString(_id), name); 076 int oldSize = _routeHashTable.size(); 077 _routeHashTable.put(route.getId(), route); 078 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, 079 _routeHashTable.size()); 080 } 081 return route; 082 } 083 084 /** 085 * Remember a NamedBean Object created outside the manager. 086 * 087 * @param route The Route to add. 088 */ 089 public void register(Route route) { 090 int oldSize = _routeHashTable.size(); 091 _routeHashTable.put(route.getId(), route); 092 // find last id created 093 int id = Integer.parseInt(route.getId()); 094 if (id > _id) { 095 _id = id; 096 } 097 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, _routeHashTable.size()); 098 // listen for name and state changes to forward 099 } 100 101 /** 102 * Forget a NamedBean Object created outside the manager. 103 * 104 * @param route The Route to delete. 105 */ 106 public void deregister(Route route) { 107 if (route == null) { 108 return; 109 } 110 route.dispose(); 111 int oldSize = _routeHashTable.size(); 112 _routeHashTable.remove(route.getId()); 113 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, _routeHashTable.size()); 114 } 115 116 /** 117 * Sort by route name 118 * 119 * @return list of routes ordered by name 120 */ 121 public List<Route> getRoutesByNameList() { 122 List<Route> sortList = getList(); 123 // now re-sort 124 List<Route> out = new ArrayList<>(); 125 for (Route route : sortList) { 126 for (int j = 0; j < out.size(); j++) { 127 if (route.getName().compareToIgnoreCase(out.get(j).getName()) < 0) { 128 out.add(j, route); 129 break; 130 } 131 } 132 if (!out.contains(route)) { 133 out.add(route); 134 } 135 } 136 return out; 137 138 } 139 140 /** 141 * Sort by route number, number can alpha numeric 142 * 143 * @return list of routes ordered by id numbers 144 */ 145 public List<Route> getRoutesByIdList() { 146 List<Route> sortList = getList(); 147 // now re-sort 148 List<Route> out = new ArrayList<>(); 149 for (Route route : sortList) { 150 for (int j = 0; j < out.size(); j++) { 151 try { 152 if (Integer.parseInt(route.getId()) < Integer.parseInt(out.get(j).getId())) { 153 out.add(j, route); 154 break; 155 } 156 } catch (NumberFormatException e) { 157 log.error("list id number isn't a number"); 158 } 159 } 160 if (!out.contains(route)) { 161 out.add(route); 162 } 163 } 164 return out; 165 } 166 167 private List<Route> getList() { 168 List<Route> out = new ArrayList<>(); 169 Enumeration<Route> en = _routeHashTable.elements(); 170 while (en.hasMoreElements()) { 171 out.add(en.nextElement()); 172 } 173 return out; 174 } 175 176 /** 177 * Used to determine if a location is part of any route. 178 * 179 * @param loc The location being checked. 180 * @return null if location isn't used, otherwise a route using the 181 * location. 182 */ 183 public Route isLocationInUse(Location loc) { 184 for (Route route : getList()) { 185 RouteLocation rl = route.getLastLocationByName(loc.getName()); 186 if (rl != null) { 187 return route; 188 } 189 } 190 return null; 191 } 192 193 public JComboBox<Route> getComboBox() { 194 JComboBox<Route> box = new JComboBox<>(); 195 box.addItem(null); 196 List<Route> routes = getRoutesByNameList(); 197 for (Route route : routes) { 198 box.addItem(route); 199 } 200 OperationsPanel.padComboBox(box, Control.max_len_string_route_name); 201 return box; 202 } 203 204 public void updateComboBox(JComboBox<Route> box) { 205 box.removeAllItems(); 206 box.addItem(null); 207 List<Route> routes = getRoutesByNameList(); 208 for (Route route : routes) { 209 box.addItem(route); 210 } 211 } 212 213 /** 214 * Copy route, returns a new route named routeName. If invert is true the 215 * reverse of the route is returned. 216 * 217 * @param route The route to be copied 218 * @param routeName The name of the new route 219 * @param invert If true, return the inversion of route 220 * @return A copy of the route 221 */ 222 public Route copyRoute(Route route, String routeName, boolean invert) { 223 Route newRoute = newRoute(routeName); 224 List<RouteLocation> routeList = route.getLocationsBySequenceList(); 225 if (!invert) { 226 for (RouteLocation rl : routeList) { 227 copyRouteLocation(newRoute, rl, null, invert); 228 } 229 // invert route order 230 } else { 231 for (int i = routeList.size() - 1; i >= 0; i--) { 232 int y = i - 1; 233 if (y < 0) { 234 y = 0; 235 } 236 copyRouteLocation(newRoute, routeList.get(i), routeList.get(y), invert); 237 } 238 } 239 newRoute.setComment(route.getComment()); 240 return newRoute; 241 } 242 243 private void copyRouteLocation(Route newRoute, RouteLocation rl, RouteLocation rlNext, boolean invert) { 244 Location loc = InstanceManager.getDefault(LocationManager.class).getLocationByName(rl.getName()); 245 RouteLocation rlNew = newRoute.addLocation(loc); 246 // now copy the route location objects we want 247 rlNew.setMaxCarMoves(rl.getMaxCarMoves()); 248 rlNew.setRandomControl(rl.getRandomControl()); 249 rlNew.setWait(rl.getWait()); 250 rlNew.setDepartureTime(rl.getDepartureTime()); 251 rlNew.setComment(rl.getComment()); 252 rlNew.setCommentColor(rl.getCommentColor()); 253 if (!invert) { 254 rlNew.setDropAllowed(rl.isDropAllowed()); 255 rlNew.setPickUpAllowed(rl.isPickUpAllowed()); 256 rlNew.setGrade(rl.getGrade()); 257 rlNew.setTrainDirection(rl.getTrainDirection()); 258 rlNew.setMaxTrainLength(rl.getMaxTrainLength()); 259 } else { 260 // flip set outs and pick ups 261 rlNew.setDropAllowed(rl.isPickUpAllowed()); 262 rlNew.setPickUpAllowed(rl.isDropAllowed()); 263 // invert train directions 264 int oldDirection = rl.getTrainDirection(); 265 if (oldDirection == RouteLocation.NORTH) { 266 rlNew.setTrainDirection(RouteLocation.SOUTH); 267 } else if (oldDirection == RouteLocation.SOUTH) { 268 rlNew.setTrainDirection(RouteLocation.NORTH); 269 } else if (oldDirection == RouteLocation.EAST) { 270 rlNew.setTrainDirection(RouteLocation.WEST); 271 } else if (oldDirection == RouteLocation.WEST) { 272 rlNew.setTrainDirection(RouteLocation.EAST); 273 } 274 // get the max length between location 275 if (rlNext == null) { 276 log.error("Can not copy route, rlNext is null!"); 277 return; 278 } 279 rlNew.setMaxTrainLength(rlNext.getMaxTrainLength()); 280 } 281 rlNew.setTrainIconX(rl.getTrainIconX()); 282 rlNew.setTrainIconY(rl.getTrainIconY()); 283 } 284 285 /** 286 * @return Number of routes 287 */ 288 public int numEntries() { 289 return _routeHashTable.size(); 290 } 291 292 public void load(Element root) { 293 // decode type, invoke proper processing routine if a decoder file 294 if (root.getChild(Xml.ROUTES) != null) { 295 List<Element> eRoutes = root.getChild(Xml.ROUTES).getChildren(Xml.ROUTE); 296 log.debug("readFile sees {} routes", eRoutes.size()); 297 for (Element eRoute : eRoutes) { 298 register(new Route(eRoute)); 299 } 300 } 301 } 302 303 public void store(Element root) { 304 Element values = new Element(Xml.ROUTES); 305 root.addContent(values); 306 for (Route route : getRoutesByIdList()) { 307 values.addContent(route.store()); 308 } 309 } 310 311 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 312 InstanceManager.getDefault(RouteManagerXml.class).setDirty(true); 313 firePropertyChange(p, old, n); 314 } 315 316 private final static Logger log = LoggerFactory.getLogger(RouteManager.class); 317 318 @Override 319 public void initialize() { 320 InstanceManager.getDefault(OperationsSetupXml.class); // load setup 321 InstanceManager.getDefault(RouteManagerXml.class); // load routes 322 } 323 324}