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