001package jmri.jmrit.logix; 002 003import java.util.ArrayList; 004import java.util.HashMap; 005import java.util.Iterator; 006import java.util.List; 007import java.util.Map; 008import java.util.TreeMap; 009 010import javax.annotation.Nonnull; 011 012import jmri.InstanceManager; 013import jmri.NamedBean; 014import jmri.ShutDownTask; 015import jmri.jmrit.roster.RosterEntry; 016import jmri.jmrit.roster.RosterSpeedProfile; 017import jmri.jmrit.roster.RosterSpeedProfile.SpeedStep; 018import jmri.jmrix.internal.InternalSystemConnectionMemo; 019import jmri.managers.AbstractManager; 020import jmri.util.ThreadingUtil; 021import jmri.util.swing.JmriJOptionPane; 022 023/** 024 * Basic Implementation of a WarrantManager. 025 * <p> 026 * Note this is a concrete class. 027 * 028 * @author Pete Cressman Copyright (C) 2009 029 */ 030public class WarrantManager extends AbstractManager<Warrant> 031 implements jmri.InstanceManagerAutoDefault { 032 033 private HashMap<String, RosterSpeedProfile> _mergeProfiles = new HashMap<>(); 034 ShutDownTask _shutDownTask = null; 035 private boolean _suppressWarnings = false; 036 037 public WarrantManager() { 038 super(InstanceManager.getDefault(InternalSystemConnectionMemo.class)); 039 } 040 041 @Override 042 public int getXMLOrder() { 043 return jmri.Manager.WARRANTS; 044 } 045 046 @Override 047 public char typeLetter() { 048 return 'W'; 049 } 050 051 /** 052 * Method to create a new Warrant if it does not exist. 053 * <p> 054 * Returns null if a Warrant with the same systemName or userName already 055 * exists, or if there is trouble creating a new Warrant. 056 * 057 * @param systemName the system name. 058 * @param userName the user name. 059 * @param SCWa true for a new SCWarrant, false for a new Warrant. 060 * @param TTP the time to platform. 061 * @return an existing warrant if found or a new warrant, may be null. 062 */ 063 public Warrant createNewWarrant(String systemName, String userName, boolean SCWa, long TTP) { 064 log.debug("createNewWarrant {} SCWa= {}",systemName,SCWa); 065 // Check that Warrant does not already exist 066 Warrant r; 067 if (userName != null && userName.trim().length() > 0) { 068 r = getByUserName(userName); 069 if (r == null) { 070 r = getBySystemName(systemName); 071 } 072 if (r != null) { 073 log.warn("Warrant {} exits.",r.getDisplayName()); 074 return null; 075 } 076 } 077 if (!systemName.startsWith(getSystemNamePrefix()) || systemName.length() < getSystemNamePrefix().length()+1) { 078 log.error("Warrant system name \"{}\" must begin with \"{}\".", 079 systemName, getSystemNamePrefix()); 080 return null; 081 } 082 // Warrant does not exist, create a new Warrant 083 if (SCWa) { 084 r = new SCWarrant(systemName, userName, TTP); 085 } else { 086 r = new Warrant(systemName, userName); 087 } 088 // save in the maps 089 register(r); 090 return r; 091 } 092 093 /** 094 * Method to get an existing Warrant. First looks up assuming that name is a 095 * User Name. If this fails looks up assuming that name is a System Name. If 096 * both fail, returns null. 097 * 098 * @param name the system name or user name 099 * @return the warrant if found or null 100 */ 101 public Warrant getWarrant(String name) { 102 Warrant r = getByUserName(name); 103 if (r != null) { 104 return r; 105 } 106 return getBySystemName(name); 107 } 108 109 public Warrant provideWarrant(String name) { 110 if (name == null || name.trim().length() == 0) { 111 return null; 112 } 113 Warrant w = getByUserName(name); 114 if (w == null) { 115 w = getBySystemName(name); 116 } 117 if (w == null) { 118 w = createNewWarrant(name, null, false, 0); 119 } 120 return w; 121 } 122 123 protected boolean okToRemoveBlock(OBlock block) { 124 String name = block.getDisplayName(); 125 List<Warrant> list = warrantsUsing(block); 126 boolean ok = true; 127 if (!list.isEmpty()) { 128// ok = false; Last setting was OK = true when _suppressWarnings was set to true 129 if (!_suppressWarnings) { 130 StringBuilder sb = new StringBuilder(); 131 for (Warrant w : list) { 132 sb.append(Bundle.getMessage("DeleteWarrantBlock", name, w.getDisplayName())); 133 } 134 sb.append(Bundle.getMessage("DeleteConfirm", name)); 135 ok = okToRemove(name, sb.toString()); 136 } 137 } 138 if (ok) { 139 removeWarrants(list); 140 } 141 return ok; 142 } 143 144 protected boolean okToRemovePortal(Portal portal) { 145 String name = portal.getName(); 146 boolean ok = true; 147 List<Warrant> wList = warrantsUsing(portal); 148 if (!wList.isEmpty()) { 149// ok = false; Last setting was OK = true when _suppressWarnings was set to true 150 if (!_suppressWarnings) { 151 StringBuilder sb = new StringBuilder(); 152 for (Warrant w : wList) { 153 sb.append(Bundle.getMessage("DeleteWarrantPortal", name, w.getDisplayName())); 154 } 155 sb.append(Bundle.getMessage("DeleteConfirm", name)); 156 ok = okToRemove(name, sb.toString()); 157 } 158 } 159 List<NamedBean> sList = signalsUsing(portal); 160 if (!sList.isEmpty()) { 161// ok = false; Last setting was OK = true when _suppressWarnings was set to true 162 if (!_suppressWarnings) { 163 StringBuilder sb = new StringBuilder(); 164 for (NamedBean s : sList) { 165 sb.append(Bundle.getMessage("DeletePortalSignal", 166 name, s.getDisplayName(), portal.getProtectedBlock(s))); 167 } 168 sb.append(Bundle.getMessage("DeleteConfirmSignal", name)); 169 ok = okToRemove(name, sb.toString()); 170 } 171 } 172 173 if (ok) { 174 removeWarrants(wList); 175 for (NamedBean s : sList) { 176 portal.deleteSignal(s); 177 } 178 } 179 return ok; 180 } 181 182 protected boolean okToRemoveBlockPath(OBlock block, OPath path) { 183 String pathName = path.getName(); 184 String blockName = block.getDisplayName(); 185 boolean ok = true; 186 List<Warrant> list = warrantsUsing(block, path); 187 if (!list.isEmpty()) { 188// ok = false; Last setting was OK = true when _suppressWarnings was set to true 189 if (!_suppressWarnings) { 190 StringBuilder sb = new StringBuilder(); 191 for (Warrant w : list) { 192 sb.append(Bundle.getMessage("DeleteWarrantPath", 193 pathName, blockName, w.getDisplayName())); 194 } 195 sb.append(Bundle.getMessage("DeleteConfirm", pathName)); 196 ok = okToRemove(pathName, sb.toString()); 197 } 198 } 199 if (ok) { 200 removeWarrants(list); 201 } 202 return ok; 203 } 204 205 private void removeWarrants(List<Warrant> list) { 206 for (Warrant w : list) { 207 if (w.getRunMode() != Warrant.MODE_NONE) { 208 w.controlRunTrain(Warrant.ABORT); 209 } 210 deregister(w); 211 w.dispose(); 212 } 213 } 214 215 private boolean okToRemove(String name, String message) { 216 if (!ThreadingUtil.isLayoutThread()) { //need GUI 217 log.warn("Cannot delete portal \"{}\" from this thread", name); 218 return false; 219 } 220 int val = JmriJOptionPane.showOptionDialog(null, message, 221 Bundle.getMessage("WarningTitle"), JmriJOptionPane.DEFAULT_OPTION, 222 JmriJOptionPane.QUESTION_MESSAGE, null, 223 new Object[]{Bundle.getMessage("ButtonYes"), 224 Bundle.getMessage("ButtonYesPlus"), 225 Bundle.getMessage("ButtonNo"),}, 226 Bundle.getMessage("ButtonNo")); // default NO 227 if (val == 2 || val == JmriJOptionPane.CLOSED_OPTION ) { // array position 2 No, or Dialog closed 228 return false; 229 } 230 if (val == 1) { // array position 1 ButtonYesPlus suppress future warnings 231 _suppressWarnings = true; 232 } 233 return true; 234 } 235 236 synchronized protected void portalNameChange(String oldName, String newName) { 237 for (Warrant w : getNamedBeanSet()) { 238 List<BlockOrder> orders = w.getBlockOrders(); 239 Iterator<BlockOrder> it = orders.iterator(); 240 while (it.hasNext()) { 241 BlockOrder bo = it.next(); 242 if (oldName.equals(bo.getEntryName())) { 243 bo.setEntryName(newName); 244 } 245 if (oldName.equals(bo.getExitName())) { 246 bo.setExitName(newName); 247 } 248 } 249 } 250 } 251 252 protected List<Warrant> warrantsUsing(OBlock block) { 253 ArrayList<Warrant> list = new ArrayList<>(); 254 for (Warrant w : getNamedBeanSet()) { 255 List<BlockOrder> orders = w.getBlockOrders(); 256 Iterator<BlockOrder> it = orders.iterator(); 257 while (it.hasNext()) { 258 if (block.equals(it.next().getBlock())) 259 list.add(w); 260 } 261 } 262 return list; 263 } 264 265 protected List<Warrant> warrantsUsing(Portal portal) { 266 ArrayList<Warrant> list = new ArrayList<>(); 267 String name = portal.getName(); 268 for (Warrant w : getNamedBeanSet()) { 269 List<BlockOrder> orders = w.getBlockOrders(); 270 Iterator<BlockOrder> it = orders.iterator(); 271 while (it.hasNext()) { 272 BlockOrder bo = it.next(); 273 if (name.equals(bo.getEntryName()) && !list.contains(w)) { 274 list.add(w); 275 } else if (name.equals(bo.getExitName()) && !list.contains(w)) { 276 list.add(w); 277 } 278 } 279 } 280 return list; 281 } 282 283 protected List<NamedBean> signalsUsing(Portal portal) { 284 ArrayList<NamedBean> list = new ArrayList<>(); 285 NamedBean signal = portal.getToSignal(); 286 if (signal != null) { 287 list.add(signal); 288 } 289 signal = portal.getFromSignal(); 290 if (signal != null) { 291 list.add(signal); 292 } 293 return list; 294 } 295 296 protected List<Warrant> warrantsUsing(OBlock block, OPath path) { 297 ArrayList<Warrant> list = new ArrayList<>(); 298 String name = path.getName(); 299 for (Warrant w : getNamedBeanSet()) { 300 List<BlockOrder> orders = w.getBlockOrders(); 301 Iterator<BlockOrder> it = orders.iterator(); 302 while (it.hasNext()) { 303 BlockOrder bo = it.next(); 304 if (block.equals(bo.getBlock()) && name.equals(bo.getPathName())) { 305 list.add(w); 306 } 307 } 308 } 309 return list; 310 } 311 312 synchronized protected void pathNameChange(OBlock block, String oldName, String newName) { 313 for (Warrant w : getNamedBeanSet()) { 314 List<BlockOrder> orders = w.getBlockOrders(); 315 Iterator<BlockOrder> it = orders.iterator(); 316 while (it.hasNext()) { 317 BlockOrder bo = it.next(); 318 if (bo.getBlock().equals(block) && bo.getPathName().equals(oldName)) { 319 bo.setPathName(newName); 320 } 321 } 322 } 323 } 324 325 /** 326 * Get the default WarrantManager. 327 * 328 * @return the default WarrantManager, creating it if necessary 329 */ 330 public static WarrantManager getDefault() { 331 return InstanceManager.getOptionalDefault(WarrantManager.class).orElseGet(() -> { 332 return InstanceManager.setDefault(WarrantManager.class, new WarrantManager()); 333 }); 334 } 335 336 @Override 337 @Nonnull 338 public String getBeanTypeHandled(boolean plural) { 339 return Bundle.getMessage(plural ? "BeanNameWarrants" : "BeanNameWarrant"); 340 } 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override 346 public Class<Warrant> getNamedBeanClass() { 347 return Warrant.class; 348 } 349 350 protected void setMergeProfile(String id, RosterSpeedProfile merge) { 351 if (_shutDownTask == null) { 352 if (!WarrantPreferences.getDefault().getShutdown().equals((WarrantPreferences.Shutdown.NO_MERGE))) { 353 _shutDownTask = new WarrantShutdownTask("WarrantRosterSpeedProfileCheck"); 354 jmri.InstanceManager.getDefault(jmri.ShutDownManager.class).register(_shutDownTask); 355 } 356 } 357 log.debug("setMergeProfile id = {}", id); 358 if (id != null && merge != null) { 359 _mergeProfiles.remove(id); 360 _mergeProfiles.put(id, merge); 361 } 362 } 363 364 /** 365 * Return a copy of the RosterSpeedProfile for Roster entry 366 * @param id roster id 367 * @return RosterSpeedProfile 368 */ 369 protected RosterSpeedProfile getMergeProfile(String id) { 370 log.debug("getMergeProfile id = {}", id); 371 return _mergeProfiles.get(id); 372 } 373 374 protected RosterSpeedProfile makeProfileCopy(RosterSpeedProfile mergeProfile, @Nonnull RosterEntry re) { 375 RosterSpeedProfile profile = new RosterSpeedProfile(re); 376 if (mergeProfile == null) { 377 mergeProfile = re.getSpeedProfile(); 378 if (mergeProfile == null) { 379 mergeProfile = new RosterSpeedProfile(re); 380 re.setSpeedProfile(mergeProfile); 381 } 382 } 383 // make copy of mergeProfile 384 TreeMap<Integer, SpeedStep> rosterTree = mergeProfile.getProfileSpeeds(); 385 for (Map.Entry<Integer, SpeedStep> entry : rosterTree.entrySet()) { 386 profile.setSpeed(entry.getKey(), entry.getValue().getForwardSpeed(), entry.getValue().getReverseSpeed()); 387 } 388 return profile; 389 } 390 391 protected HashMap<String, RosterSpeedProfile> getMergeProfiles() { 392 return _mergeProfiles; 393 } 394 395 @Override 396 public void dispose(){ 397 for(Warrant w:_beans){ 398 w.dispose(); 399 } 400 super.dispose(); 401 } 402 403 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(WarrantManager.class); 404 405}