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