001package jmri.implementation; 002 003import java.util.ArrayList; 004import java.util.HashMap; 005import java.util.List; 006import java.util.regex.Pattern; 007 008import javax.annotation.*; 009 010import jmri.*; 011import jmri.jmrit.beantable.LRouteTableAction; 012import jmri.jmrit.entryexit.EntryExitPairs; 013 014/** 015 * Class providing the basic logic of the Logix interface. 016 * 017 * @author Dave Duchamp Copyright (C) 2007 018 * @author Pete Cressman Copyright (C) 2009 019 */ 020public class DefaultLogix extends AbstractNamedBean 021 implements Logix { 022 023 private final ConditionalManager conditionalManager; 024 025 public DefaultLogix(String systemName, String userName) { 026 this(systemName,userName,InstanceManager.getDefault(ConditionalManager.class)); 027 } 028 029 public DefaultLogix(String systemName,String userName,ConditionalManager conditionalManager) { 030 super(systemName, userName); 031 this.conditionalManager = conditionalManager; 032 } 033 034 public DefaultLogix(String systemName) { 035 this(systemName,InstanceManager.getDefault(ConditionalManager.class)); 036 } 037 038 public DefaultLogix(String systemName,ConditionalManager conditionalManager) { 039 super(systemName); 040 this.conditionalManager = conditionalManager; 041 } 042 043 @Override 044 @Nonnull 045 public String getBeanType() { 046 return Bundle.getMessage("BeanNameLogix"); // NOI18N 047 } 048 049 /** 050 * Persistant instance variables (saved between runs). Order is significant. 051 */ 052 private final ArrayList<String> _conditionalSystemNames = new ArrayList<>(); 053 private ArrayList<JmriSimplePropertyListener> _listeners = new ArrayList<>(); 054 055 /** 056 * Maintain a list of conditional objects. The key is the conditional system name 057 * @since 4.7.4 058 */ 059 private final HashMap<String, Conditional> _conditionalMap = new HashMap<>(); 060 061 /** 062 * Operational instance variables (not saved between runs) 063 */ 064 private boolean mEnabled = true; 065 066 private boolean _isActivated = false; 067 068 private boolean _isGuiSet = false; 069 070 /** 071 * Get number of Conditionals for this Logix 072 */ 073 @Override 074 public int getNumConditionals() { 075 return _conditionalSystemNames.size(); 076 } 077 078 /** 079 * Move 'row' to 'nextInOrder' and shift all between 'row' and 'nextInOrder' 080 * up one position {@literal ( row > nextInOrder )} 081 */ 082 @Override 083 public void swapConditional(int nextInOrder, int row) { 084 if (row <= nextInOrder) { 085 return; 086 } 087 String temp = _conditionalSystemNames.get(row); 088 for (int i = row; i > nextInOrder; i--) { 089 _conditionalSystemNames.set(i, _conditionalSystemNames.get(i - 1)); 090 } 091 _conditionalSystemNames.set(nextInOrder, temp); 092 } 093 094 /** 095 * Returns the system name of the conditional that will calculate in the 096 * specified order. This is also the order the Conditional is listed in the 097 * Add/Edit Logix dialog. If 'order' is greater than the number of 098 * Conditionals for this Logix, null is returned. 099 * 100 * @param order order in which the Conditional calculates. 101 */ 102 @Override 103 @CheckForNull 104 public String getConditionalByNumberOrder(int order) { 105 try { 106 return _conditionalSystemNames.get(order); 107 } catch (java.lang.IndexOutOfBoundsException ioob) { 108 return null; 109 } 110 } 111 112 /** 113 * Add a Conditional to this Logix R 114 * 115 * @param systemName The Conditional system name 116 * @param order the order this conditional should calculate in if 117 * order is negative, the conditional is added at the end 118 * of current group of conditionals 119 */ 120 @Override 121 public void addConditional(String systemName, int order) { 122 _conditionalSystemNames.add(systemName); 123 } 124 125 /** 126 * Add a child Conditional to the parent Logix. 127 * 128 * @since 4.7.4 129 * @param systemName The system name for the Conditional object. 130 * @param conditional The Conditional object. 131 * @return true if the Conditional was added, false otherwise. 132 */ 133 @Override 134 public boolean addConditional(String systemName, Conditional conditional) { 135 Conditional chkDuplicate = _conditionalMap.putIfAbsent(systemName, conditional); 136 if (chkDuplicate == null) { 137 return true; 138 } 139 log.error("Conditional '{}' has already been added to Logix '{}'", systemName, getSystemName()); // NOI18N 140 return false; 141 } 142 143 /** 144 * Get a Conditional belonging to this Logix. 145 * 146 * @since 4.7.4 147 * @param systemName The name of the Conditional object. 148 * @return the Conditional object or null if not found. 149 */ 150 @Override 151 @CheckForNull 152 public Conditional getConditional(String systemName) { 153 return _conditionalMap.get(systemName); 154 } 155 156 /** 157 * Set enabled status. Enabled is a bound property All conditionals are set 158 * to UNKNOWN state and recalculated when the Logix is enabled, provided the 159 * Logix has been previously activated. 160 * <p> 161 * Enabling a Logix that has not been activated (for example one loaded 162 * disabled) activates it, so that a later deActivateLogix() removes its 163 * listeners again. 164 */ 165 @Override 166 public void setEnabled(boolean state) { 167 168 boolean old = mEnabled; 169 mEnabled = state; 170 if (old != state) { 171 if (_isActivated || state) { 172 deActivateLogix(); 173 activateLogix(); 174 } 175 for (int i = _listeners.size() - 1; i >= 0; i--) { 176 _listeners.get(i).setEnabled(state); 177 } 178 firePropertyChange(PROPERTY_ENABLED, old, state); 179 } 180 } 181 182 /** 183 * Get enabled status 184 */ 185 @Override 186 public boolean getEnabled() { 187 return mEnabled; 188 } 189 190 /** 191 * Delete a Conditional and remove it from this Logix 192 * <p> 193 * Note: Since each Logix must have at least one Conditional to do anything, 194 * the user is warned in Logix Table Action when the last Conditional is 195 * deleted. 196 * 197 * @param systemName The Conditional system name 198 * @return null if Conditional was successfully deleted or not present, otherwise 199 * returns a string array list of current usage that prevent deletion, used to present 200 * a warning dialog to the user 201 */ 202 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS", 203 justification = "null returned is documented in each method to mean completed without problems") 204 @Override 205 public String[] deleteConditional(String systemName) { 206 if (_conditionalSystemNames.isEmpty()) { 207 return null; 208 } 209 210 // check other Logix(es) for use of this conditional (systemName) for use as a 211 // variable in one of their conditionals 212 ArrayList<String> checkReferences = conditionalManager.getWhereUsed(systemName); 213 if (checkReferences != null) { 214 Conditional c = getConditional(systemName); 215 String refName = checkReferences.get(0); 216 Logix x = conditionalManager.getParentLogix(refName); 217 if ( x == null ) { 218 log.error("Unable to get Parent Logix {} while deleting Conditional {}", 219 refName, systemName); 220 return null; 221 } 222 Conditional cRef = x.getConditional(refName); 223 return new String[]{c.getUserName(), c.getSystemName(), cRef.getUserName(), 224 cRef.getSystemName(), x.getUserName(), x.getSystemName()}; 225 } 226 227 // Confirm the presence of the Conditional object 228 Conditional c = conditionalManager.getBySystemName(systemName); 229 if (c == null) { 230 log.error("attempt to delete non-existing Conditional - {}", systemName); // NOI18N 231 return null; 232 } 233 234 // Remove Conditional from this logix 235 if (!_conditionalSystemNames.remove(systemName)) { 236 log.error("attempt to delete Conditional not in Logix: {}", systemName); // NOI18N 237 return null; 238 } 239 240 _conditionalMap.remove(systemName); 241 return null; 242 } 243 244 /** 245 * Calculate all Conditionals, triggering action if the user specified 246 * conditions are met, and the Logix is enabled. 247 */ 248 @Override 249 public void calculateConditionals() { 250 for (String conditionalSystemName : _conditionalSystemNames) { 251 Conditional c = getConditional(conditionalSystemName); 252 if (c == null) { 253 log.error("Invalid conditional system name when calculating Logix - {}", 254 conditionalSystemName); 255 } else { 256 // calculate without taking any action unless Logix is enabled 257 c.calculate(mEnabled, null); 258 } 259 } 260 } 261 262 /** 263 * Activate the Logix, starts Logix processing by connecting all inputs that 264 * are included the Conditionals in this Logix. 265 * <p> 266 * A Logix must be activated before it will calculate any of its 267 * Conditionals. 268 */ 269 @Override 270 public void activateLogix() { 271 // if the Logix is already busy, simply return 272 if (_isActivated) { 273 return; 274 } 275 // set the state of all Conditionals to UNKNOWN 276 resetConditionals(); 277 // assemble a list of needed listeners 278 assembleListenerList(); 279 // create and attach the needed property change listeners 280 // start a minute Listener if needed 281 for (JmriSimplePropertyListener listener : _listeners) { 282 startListener(listener); 283 } 284 // mark this Logix as busy 285 _isActivated = true; 286 // calculate this Logix to set initial state of Conditionals 287 calculateConditionals(); 288 } 289 290 private void resetConditionals() { 291 for (String conditionalSystemName : _conditionalSystemNames) { 292 Conditional conditional = getConditional(conditionalSystemName); 293 if (conditional != null) { 294 try { 295 conditional.setState(NamedBean.UNKNOWN); 296 } catch (JmriException ignore) { 297 } 298 } 299 } 300 } 301 302 // Pattern to check for new style NX system name 303 static final Pattern NXUUID = Pattern.compile( 304 "^IN:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", // NOI18N 305 Pattern.CASE_INSENSITIVE); 306 307 /** 308 * ConditionalVariables only have a single name field. For user interface purposes 309 * a gui name is used for the referenced conditional user name. This is not used 310 * for other object types. 311 * <p> 312 * In addition to setting the GUI name, any state variable references are changed to 313 * conditional system names. This converts the XML system/user name field to the system name 314 * for conditional references. It does not affect other objects such as sensors, turnouts, etc. 315 * <p> 316 * For Entry/Exit references, replace NX user names and old style NX UUID references 317 * with the new style "IN:" + UUID reference. If the referenced NX does not exist, 318 * it will be removed from the Variable or Action list. (4.11.4) 319 * <p> 320 * Called by {@link jmri.managers.DefaultLogixManager#activateAllLogixs} 321 * @since 4.7.4 322 */ 323 @Override 324 public void setGuiNames() { 325 if (_isGuiSet) { 326 return; 327 } 328 if ("SYS".equals(getSystemName())) { 329 _isGuiSet = true; 330 return; 331 } 332 for (String cName : _conditionalSystemNames) { 333 Conditional conditional = getConditional(cName); 334 if (conditional == null) { 335 // A Logix index entry exists without a corresponding conditional. This 336 // should never happen. 337 log.error("setGuiNames: Missing conditional for Logix index entry, " 338 + " Logix name = '{}', Conditional index name = '{}'", // NOI18N 339 getSystemName(), cName); 340 continue; 341 } 342 List<ConditionalVariable> varList = conditional.getCopyOfStateVariables(); 343 boolean isDirty = false; 344 ArrayList<ConditionalVariable> badVariable = new ArrayList<>(); 345 for (ConditionalVariable condVar : varList) { 346 // Find any Conditional State Variables 347 if (condVar.getType() == Conditional.Type.CONDITIONAL_TRUE 348 || condVar.getType() == Conditional.Type.CONDITIONAL_FALSE) { 349 // Get the referenced (target) conditonal -- The name can be either a system name or a user name 350 Conditional cRef = conditionalManager.getConditional(condVar.getName()); 351 if (cRef != null) { 352 // re-arrange names as needed 353 condVar.setName(cRef.getSystemName()); // The state variable reference is now a conditional system name 354 String uName = cRef.getUserName(); 355 if (uName == null || uName.isEmpty()) { 356 condVar.setGuiName(cRef.getSystemName()); 357 } else { 358 condVar.setGuiName(uName); 359 } 360 // Add the conditional reference to the where used map 361 conditionalManager.addWhereUsed(condVar.getName(), cName); 362 isDirty = true; 363 } else { 364 log.error("setGuiNames: For conditional '{}' in logix '{}', " 365 + "the referenced conditional, '{}', does not exist", 366 cName, getSystemName(), condVar.getName()); 367 } 368 } 369 370 // Find any Entry/Exit State Variables 371 if (condVar.getType() == Conditional.Type.ENTRYEXIT_ACTIVE 372 || condVar.getType() == Conditional.Type.ENTRYEXIT_INACTIVE) { 373 if (!NXUUID.matcher(condVar.getName()).find()) { 374 // Either a user name or an old style system name (plain UUID) 375 jmri.jmrit.entryexit.DestinationPoints dp = 376 InstanceManager.getDefault(EntryExitPairs.class). 377 getNamedBean(condVar.getName()); 378 if (dp != null) { 379 // Replace name with current system name 380 condVar.setName(dp.getSystemName()); 381 isDirty = true; 382 } else { 383 log.error("setGuiNames: For conditional '{}' in logix '{}', " 384 + "the referenced Entry Exit Pair, '{}', does not exist", 385 cName, getSystemName(), condVar.getName()); 386 badVariable.add(condVar); 387 } 388 } 389 } 390 } 391 if (!badVariable.isEmpty()) { 392 isDirty = true; 393 badVariable.forEach(varList::remove); 394 } 395 if (isDirty) { 396 conditional.setStateVariables(varList); 397 } 398 399 List<ConditionalAction> actionList = conditional.getCopyOfActions(); 400 isDirty = false; 401 ArrayList<ConditionalAction> badAction = new ArrayList<>(); 402 for (ConditionalAction action : actionList) { 403 // Find any Entry/Exit Actions 404 if (action.getType() == Conditional.Action.SET_NXPAIR_ENABLED 405 || action.getType() == Conditional.Action.SET_NXPAIR_DISABLED 406 || action.getType() == Conditional.Action.SET_NXPAIR_SEGMENT) { 407 if (!NXUUID.matcher(action.getDeviceName()).find()) { 408 // Either a user name or an old style system name (plain UUID) 409 jmri.jmrit.entryexit.DestinationPoints dp = 410 InstanceManager.getDefault(EntryExitPairs.class). 411 getNamedBean(action.getDeviceName()); 412 if (dp != null) { 413 // Replace name with current system name 414 action.setDeviceName(dp.getSystemName()); 415 isDirty = true; 416 } else { 417 log.error("setGuiNames: For conditional '{}' in logix '{}', " 418 + "the referenced Entry Exit Pair, '{}', does not exist", // NOI18N 419 cName, getSystemName(), action.getDeviceName()); 420 badAction.add(action); 421 } 422 } 423 } 424 } 425 if (!badAction.isEmpty()) { 426 isDirty = true; 427 badAction.forEach(actionList::remove); 428 } 429 if (isDirty) { 430 conditional.setAction(actionList); 431 } 432 } 433 _isGuiSet = true; 434 } 435 436 /** 437 * Assemble a list of Listeners needed to activate this Logix. 438 */ 439 private void assembleListenerList() { 440 // initialize by cleaning up 441 // start from end down to safely delete preventing concurrent modification ex 442 for (int i = _listeners.size() - 1; i >= 0; i--) { 443 removeListener(_listeners.get(i)); 444 } 445 _listeners = new ArrayList<>(); 446 // cycle thru Conditionals to find objects to listen to 447 for (int i = 0; i < _conditionalSystemNames.size(); i++) { 448 Conditional conditional = getConditional(_conditionalSystemNames.get(i)); 449 if (conditional != null) { 450 List<ConditionalVariable> variableList = conditional.getCopyOfStateVariables(); 451 for (ConditionalVariable variable : variableList) { 452 // check if listening for a change has been suppressed 453 int varListenerType = 0; 454 String varName = variable.getName(); 455 NamedBeanHandle<?> namedBean = variable.getNamedBean(); 456 Conditional.Type varType = variable.getType(); 457 int signalAspect = -1; 458 // Get Listener type from variable type 459 switch (varType) { 460 case SENSOR_ACTIVE: 461 case SENSOR_INACTIVE: 462 varListenerType = LISTENER_TYPE_SENSOR; 463 break; 464 case TURNOUT_THROWN: 465 case TURNOUT_CLOSED: 466 varListenerType = LISTENER_TYPE_TURNOUT; 467 break; 468 case CONDITIONAL_TRUE: 469 case CONDITIONAL_FALSE: 470 varListenerType = LISTENER_TYPE_CONDITIONAL; 471 break; 472 case LIGHT_ON: 473 case LIGHT_OFF: 474 varListenerType = LISTENER_TYPE_LIGHT; 475 break; 476 case MEMORY_EQUALS: 477 case MEMORY_COMPARE: 478 case MEMORY_EQUALS_INSENSITIVE: 479 case MEMORY_COMPARE_INSENSITIVE: 480 varListenerType = LISTENER_TYPE_MEMORY; 481 break; 482 case ROUTE_FREE: 483 case ROUTE_OCCUPIED: 484 case ROUTE_ALLOCATED: 485 case ROUTE_SET: 486 case TRAIN_RUNNING: 487 varListenerType = LISTENER_TYPE_WARRANT; 488 break; 489 case FAST_CLOCK_RANGE: 490 varListenerType = LISTENER_TYPE_FASTCLOCK; 491 varName = "clock"; // NOI18N 492 break; 493 case SIGNAL_HEAD_RED: 494 varListenerType = LISTENER_TYPE_SIGNALHEAD; 495 signalAspect = SignalHead.RED; 496 break; 497 case SIGNAL_HEAD_YELLOW: 498 varListenerType = LISTENER_TYPE_SIGNALHEAD; 499 signalAspect = SignalHead.YELLOW; 500 break; 501 case SIGNAL_HEAD_GREEN: 502 varListenerType = LISTENER_TYPE_SIGNALHEAD; 503 signalAspect = SignalHead.GREEN; 504 break; 505 case SIGNAL_HEAD_DARK: 506 varListenerType = LISTENER_TYPE_SIGNALHEAD; 507 signalAspect = SignalHead.DARK; 508 break; 509 case SIGNAL_HEAD_LUNAR: 510 varListenerType = LISTENER_TYPE_SIGNALHEAD; 511 signalAspect = SignalHead.LUNAR; 512 break; 513 case SIGNAL_HEAD_FLASHRED: 514 varListenerType = LISTENER_TYPE_SIGNALHEAD; 515 signalAspect = SignalHead.FLASHRED; 516 break; 517 case SIGNAL_HEAD_FLASHYELLOW: 518 varListenerType = LISTENER_TYPE_SIGNALHEAD; 519 signalAspect = SignalHead.FLASHYELLOW; 520 break; 521 case SIGNAL_HEAD_FLASHGREEN: 522 varListenerType = LISTENER_TYPE_SIGNALHEAD; 523 signalAspect = SignalHead.FLASHGREEN; 524 break; 525 case SIGNAL_HEAD_FLASHLUNAR: 526 varListenerType = LISTENER_TYPE_SIGNALHEAD; 527 signalAspect = SignalHead.FLASHLUNAR; 528 break; 529 case SIGNAL_HEAD_LIT: 530 case SIGNAL_HEAD_HELD: 531 varListenerType = LISTENER_TYPE_SIGNALHEAD; 532 break; 533 case SIGNAL_MAST_ASPECT_EQUALS: 534 case SIGNAL_MAST_LIT: 535 case SIGNAL_MAST_HELD: 536 varListenerType = LISTENER_TYPE_SIGNALMAST; 537 break; 538 case BLOCK_STATUS_EQUALS: 539 varListenerType = LISTENER_TYPE_OBLOCK; 540 break; 541 case ENTRYEXIT_ACTIVE: 542 case ENTRYEXIT_INACTIVE: 543 varListenerType = LISTENER_TYPE_ENTRYEXIT; 544 break; 545 default: 546 if (!LRouteTableAction.getLogixInitializer().equals(varName)) { 547 log.warn("Unhandled conditional variable type: {}", varType); // NOI18N 548 } 549 break; 550 } 551 int positionOfListener = getPositionOfListener(varListenerType, varType, varName); 552 // add to list if new 553 JmriSimplePropertyListener listener; 554 if (positionOfListener == -1) { 555 switch (varListenerType) { 556 case LISTENER_TYPE_SENSOR: 557 listener = new JmriTwoStatePropertyListener(Sensor.PROPERTY_KNOWN_STATE, 558 LISTENER_TYPE_SENSOR, namedBean, varType, conditional); 559 break; 560 case LISTENER_TYPE_TURNOUT: 561 listener = new JmriTwoStatePropertyListener(Turnout.PROPERTY_KNOWN_STATE, 562 LISTENER_TYPE_TURNOUT, namedBean, varType, conditional); 563 break; 564 case LISTENER_TYPE_CONDITIONAL: 565 listener = new JmriTwoStatePropertyListener(Conditional.PROPERTY_KNOWN_STATE, 566 LISTENER_TYPE_CONDITIONAL, namedBean, varType, conditional); 567 break; 568 case LISTENER_TYPE_LIGHT: 569 listener = new JmriTwoStatePropertyListener(Light.PROPERTY_KNOWN_STATE, 570 LISTENER_TYPE_LIGHT, namedBean, varType, conditional); 571 break; 572 case LISTENER_TYPE_MEMORY: 573 listener = new JmriTwoStatePropertyListener(Memory.PROPERTY_VALUE, 574 LISTENER_TYPE_MEMORY, namedBean, varType, conditional); 575 break; 576 case LISTENER_TYPE_WARRANT: 577 listener = new JmriSimplePropertyListener(null, 578 LISTENER_TYPE_WARRANT, namedBean, varType, conditional); 579 break; 580 case LISTENER_TYPE_FASTCLOCK: 581 listener = new JmriClockPropertyListener(Timebase.PROPERTY_CHANGE_MINUTES, 582 LISTENER_TYPE_FASTCLOCK, varName, varType, conditional, 583 variable.getNum1(), variable.getNum2()); 584 break; 585 case LISTENER_TYPE_SIGNALHEAD: 586 if (signalAspect < 0) { 587 if (varType == Conditional.Type.SIGNAL_HEAD_LIT) { 588 listener = new JmriTwoStatePropertyListener(SignalHead.PROPERTY_LIT, 589 LISTENER_TYPE_SIGNALHEAD, namedBean, varType, conditional); 590 } else { // varType == Conditional.TYPE_SIGNAL_HEAD_HELD 591 listener = new JmriTwoStatePropertyListener(SignalHead.PROPERTY_HELD, 592 LISTENER_TYPE_SIGNALHEAD, namedBean, varType, conditional); 593 } 594 } else { 595 listener = new JmriMultiStatePropertyListener(SignalHead.PROPERTY_APPEARANCE, 596 LISTENER_TYPE_SIGNALHEAD, namedBean, varType, conditional, signalAspect); 597 } 598 break; 599 case LISTENER_TYPE_SIGNALMAST: 600 switch (varType) { 601 case SIGNAL_MAST_LIT: 602 listener = new JmriTwoStatePropertyListener(SignalMast.PROPERTY_LIT, 603 LISTENER_TYPE_SIGNALMAST, namedBean, varType, conditional); 604 break; 605 case SIGNAL_MAST_HELD: 606 listener = new JmriTwoStatePropertyListener(SignalMast.PROPERTY_HELD, 607 LISTENER_TYPE_SIGNALMAST, namedBean, varType, conditional); 608 break; 609 default: 610 listener = new JmriTwoStatePropertyListener(SignalMast.PROPERTY_ASPECT, 611 LISTENER_TYPE_SIGNALMAST, namedBean, varType, conditional); 612 break; 613 } 614 break; 615 case LISTENER_TYPE_OBLOCK: 616 listener = new JmriTwoStatePropertyListener(jmri.jmrit.logix.OBlock.PROPERTY_STATE, 617 LISTENER_TYPE_OBLOCK, namedBean, varType, conditional); 618 break; 619 case LISTENER_TYPE_ENTRYEXIT: 620 listener = new JmriTwoStatePropertyListener(EntryExitPairs.PROPERTY_ACTIVE, 621 LISTENER_TYPE_ENTRYEXIT, namedBean, varType, conditional); 622 break; 623 default: 624 if (!LRouteTableAction.getLogixInitializer().equals(varName)) { 625 log.error("Unknown (new) Variable Listener type= {}, for varName= {}, varType= {} in Conditional, {}", 626 varListenerType, varName, varType, _conditionalSystemNames.get(i)); 627 } 628 continue; 629 } 630 _listeners.add(listener); 631 } else { 632 switch (varListenerType) { 633 case LISTENER_TYPE_SENSOR: 634 case LISTENER_TYPE_TURNOUT: 635 case LISTENER_TYPE_CONDITIONAL: 636 case LISTENER_TYPE_LIGHT: 637 case LISTENER_TYPE_MEMORY: 638 case LISTENER_TYPE_WARRANT: 639 case LISTENER_TYPE_SIGNALMAST: 640 case LISTENER_TYPE_OBLOCK: 641 case LISTENER_TYPE_ENTRYEXIT: 642 listener = _listeners.get(positionOfListener); 643 listener.addConditional(conditional); 644 break; 645 case LISTENER_TYPE_FASTCLOCK: 646 JmriClockPropertyListener cpl = (JmriClockPropertyListener) _listeners.get(positionOfListener); 647 cpl.setRange(variable.getNum1(), variable.getNum2()); 648 cpl.addConditional(conditional); 649 break; 650 case LISTENER_TYPE_SIGNALHEAD: 651 if (signalAspect < 0) { 652 listener = _listeners.get(positionOfListener); 653 listener.addConditional(conditional); 654 } else { 655 JmriMultiStatePropertyListener mpl = (JmriMultiStatePropertyListener) _listeners.get(positionOfListener); 656 mpl.addConditional(conditional); 657 mpl.setState(signalAspect); 658 } 659 break; 660 default: 661 log.error("Unknown (old) Variable Listener type= {}, for varName= {}, varType= {} in Conditional, {}", 662 varListenerType, varName, varType, _conditionalSystemNames.get(i)); 663 } 664 } 665 // addition listeners needed for memory compare 666 if (varType == Conditional.Type.MEMORY_COMPARE || varType == Conditional.Type.MEMORY_COMPARE_INSENSITIVE) { 667 positionOfListener = getPositionOfListener(varListenerType, varType, variable.getDataString()); 668 if (positionOfListener == -1) { 669 String name = variable.getDataString(); 670 try { 671 Memory my = InstanceManager.memoryManagerInstance().provideMemory(name); 672 NamedBeanHandle<?> nb = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(name, my); 673 674 listener = new JmriTwoStatePropertyListener(Memory.PROPERTY_VALUE, LISTENER_TYPE_MEMORY, 675 nb, varType, conditional); 676 _listeners.add(listener); 677 } catch (IllegalArgumentException ex) { 678 log.error("invalid memory name= \"{}\" in state variable", name); // NOI18N 679 break; 680 } 681 } else { 682 listener = _listeners.get(positionOfListener); 683 listener.addConditional(conditional); 684 } 685 } 686 } 687 } else { 688 log.error("invalid conditional system name in Logix \"{}\" " 689 + "assembleListenerList DELETING {} from Conditional list.", 690 getSystemName(), _conditionalSystemNames.get(i)); 691 _conditionalSystemNames.remove(i); 692 } 693 } 694 } 695 696 private int getPositionOfListener(int varListenerType, Conditional.Type varType, String varName) { 697 // check if already in list 698 for (int j = 0; (j < _listeners.size()); j++) { 699 if (varListenerType == _listeners.get(j).getType()) { 700 if (varName.equals(_listeners.get(j).getDevName())) { 701 if (varListenerType == LISTENER_TYPE_SIGNALHEAD) { 702 if (varType == Conditional.Type.SIGNAL_HEAD_LIT 703 || varType == Conditional.Type.SIGNAL_HEAD_HELD) { 704 if (varType == _listeners.get(j).getVarType()) { 705 return j; 706 } 707 } else if (SignalHead.PROPERTY_APPEARANCE.equals(_listeners.get(j).getPropertyName())) { 708 // the Appearance Listener can handle all aspects 709 return j; 710 } 711 } else { 712 return j; 713 } 714 } 715 } 716 717 } 718 return -1; 719 } 720 721 /* /** 722 * Assembles and returns a list of state variables that are used by 723 * conditionals of this Logix including the number of occurances of each 724 * variable that trigger a calculation, and the number of occurances where 725 * the triggering has been suppressed. The main use of this method is to 726 * return information that can be used to test for inconsistency in 727 * suppressing triggering of a calculation among multiple occurances of the 728 * same state variable. Caller provides an ArrayList of the variables to 729 * check and an empty Array list to return the counts for triggering or 730 * suppressing calculation. The first index is a count that the 731 * correspondeing variable triggers calculation and second is a count that 732 * the correspondeing variable suppresses Calculation. Note this method must 733 * not modify the supplied variable list in any way. 734 * 735 * public void getStateVariableList(ArrayList <ConditionalVariable> varList, 736 * ArrayList <int[]> triggerPair) { // initialize Conditional c = null; 737 * String testSystemName = ""; String testUserName = ""; String testVarName 738 * = ""; // cycle thru Conditionals to find state variables 739 * ConditionalManager cm = InstanceManager.getDefault(jmri.ConditionalManager.class); for 740 * (int i=0; i<_conditionalSystemNames.size(); i++) { c = 741 * cm.getBySystemName(_conditionalSystemNames.get(i)); if (c!=null) { 742 * ArrayList variableList = c.getCopyOfStateVariables(); for (int k = 0; 743 * k<variableList.size(); k++) { ConditionalVariable variable = 744 * (ConditionalVariable)variableList.get(k); testVarName = 745 * variable.getName(); testSystemName = ""; testUserName = ""; // initialize 746 * this state variable switch (variable.getType()) { case 747 * Conditional.TYPE_SENSOR_ACTIVE: case Conditional.TYPE_SENSOR_INACTIVE: 748 * Sensor s = InstanceManager.sensorManagerInstance(). 749 * getSensor(testVarName); if (s!=null) { testSystemName = 750 * s.getSystemName(); testUserName = s.getUserName(); } break; case 751 * Conditional.TYPE_TURNOUT_THROWN: case Conditional.TYPE_TURNOUT_CLOSED: 752 * Turnout t = InstanceManager.turnoutManagerInstance(). 753 * getTurnout(testVarName); if (t!=null) { testSystemName = 754 * t.getSystemName(); testUserName = t.getUserName(); } break; case 755 * Conditional.TYPE_CONDITIONAL_TRUE: case 756 * Conditional.TYPE_CONDITIONAL_FALSE: Conditional cx = 757 * InstanceManager.getDefault(jmri.ConditionalManager.class). 758 * getConditional(this,testVarName); if (cx==null) { cx = 759 * InstanceManager.getDefault(jmri.ConditionalManager.class). 760 * getBySystemName(testVarName); } if (cx!=null) { testSystemName = 761 * cx.getSystemName(); testUserName = cx.getUserName(); } break; case 762 * Conditional.TYPE_LIGHT_ON: case Conditional.TYPE_LIGHT_OFF: Light lgt = 763 * InstanceManager.lightManagerInstance(). getLight(testVarName); if 764 * (lgt!=null) { testSystemName = lgt.getSystemName(); testUserName = 765 * lgt.getUserName(); } break; case Conditional.TYPE_MEMORY_EQUALS: Memory m 766 * = InstanceManager.memoryManagerInstance(). getMemory(testVarName); if 767 * (m!=null) { testSystemName = m.getSystemName(); testUserName = 768 * m.getUserName(); } break; case Conditional.TYPE_SIGNAL_HEAD_RED: case 769 * Conditional.TYPE_SIGNAL_HEAD_YELLOW: case 770 * Conditional.TYPE_SIGNAL_HEAD_GREEN: case 771 * Conditional.TYPE_SIGNAL_HEAD_DARK: case 772 * Conditional.TYPE_SIGNAL_HEAD_FLASHRED: case 773 * Conditional.TYPE_SIGNAL_HEAD_FLASHYELLOW: case 774 * Conditional.TYPE_SIGNAL_HEAD_FLASHGREEN: SignalHead h = 775 * InstanceManager.getDefault(jmri.SignalHeadManager.class). getSignalHead(testVarName); 776 * if (h!=null) { testSystemName = h.getSystemName(); testUserName = 777 * h.getUserName(); } break; case Conditional.TYPE_SIGNAL_HEAD_LIT: 778 * SignalHead hx = InstanceManager.getDefault(jmri.SignalHeadManager.class). 779 * getSignalHead(testVarName); if (hx!=null) { testSystemName = 780 * hx.getSystemName(); testUserName = hx.getUserName(); } break; case 781 * Conditional.TYPE_SIGNAL_HEAD_HELD: SignalHead hy = 782 * InstanceManager.getDefault(jmri.SignalHeadManager.class). getSignalHead(testVarName); 783 * if (hy!=null) { testSystemName = hy.getSystemName(); testUserName = 784 * hy.getUserName(); } break; default: testSystemName = ""; } // check if 785 * this state variable is already in the list to be returned boolean inList 786 * = false; int indexOfRepeat = -1; if (testSystemName!="") { // getXXXXXX 787 * succeeded, process this state variable for (int j=0; j<varList.size(); 788 * j++) { ConditionalVariable v = varList.get(j); if ( 789 * v.getName().equals(testSystemName) || v.getName().equals(testUserName) ) 790 * { inList = true; indexOfRepeat = j; break; } } // add to list if new and 791 * if there is room if ( inList ) { int[] trigs = 792 * triggerPair.get(indexOfRepeat); if ( variable.doCalculation() ) { 793 * trigs[0]++; } else { trigs[1]++; 794 * 795 * } 796 * } 797 * } 798 * } 799 * } 800 * else { log.error("invalid conditional system name in Logix 801 * getStateVariableList - "+ _conditionalSystemNames.get(i)); 802 * 803 * } 804 * } 805 * } // getStateVariableList 806 */ 807 808 /** 809 * Deactivate the Logix. This method disconnects the Logix from all input 810 * objects and stops it from being triggered to calculate. 811 * <p> 812 * A Logix must be deactivated before its Conditionals are changed. 813 */ 814 @Override 815 public void deActivateLogix() { 816 if (_isActivated) { 817 // Logix is active, deactivate it and all listeners 818 _isActivated = false; 819 // remove listeners if there are any 820 for (int i = _listeners.size() - 1; i >= 0; i--) { 821 removeListener(_listeners.get(i)); 822 } 823 } 824 } 825 826 /** 827 * Creates a listener of the required type and starts it 828 */ 829 private void startListener(JmriSimplePropertyListener listener) { 830 831 if (listener.getType() == LISTENER_TYPE_FASTCLOCK) { 832 Timebase tb = InstanceManager.getDefault(Timebase.class); 833 tb.addMinuteChangeListener(listener); 834 } else { 835 NamedBeanHandle<?> namedBeanHandle = listener.getNamedBean(); 836 if (namedBeanHandle == null) { 837 log.error("Bad name for {} '{}' when setting up Logix listener [ {} ]", 838 getListenerTypeName(listener.getType()), listener.getDevName(), this.getSystemName()); 839 } else { 840 NamedBean nb = namedBeanHandle.getBean(); 841 nb.addPropertyChangeListener(listener, namedBeanHandle.getName(), 842 "Logix " + getDisplayName()); 843 } 844 } 845 } 846 847 /** 848 * Remove a listener of the required type 849 */ 850 private void removeListener(JmriSimplePropertyListener listener) { 851 String typeName = null; 852 NamedBean nb; 853 NamedBeanHandle<?> namedBeanHandle; 854 try { 855 switch (listener.getType()) { 856 case LISTENER_TYPE_FASTCLOCK: 857 Timebase tb = InstanceManager.getDefault(Timebase.class); 858 tb.removeMinuteChangeListener(listener); 859 return; 860 case LISTENER_TYPE_ENTRYEXIT: 861 NamedBean ex = InstanceManager.getDefault(EntryExitPairs.class) 862 .getNamedBean(listener.getDevName()); 863 if (ex == null) { 864 typeName = "entryexit"; // NOI18N 865 break; 866 } 867 ex.removePropertyChangeListener(listener); 868 return; 869 default: 870 namedBeanHandle = listener.getNamedBean(); 871 if (namedBeanHandle == null) { 872 typeName = getListenerTypeName(listener.getType()); 873 break; 874 } 875 nb = namedBeanHandle.getBean(); 876 nb.removePropertyChangeListener(listener); 877 return; 878 } 879 } catch (Exception ex) { 880 log.error("Bad name for listener on \"{}\": ", listener.getDevName(), ex); // NOI18N 881 } 882 log.error("Bad name for {} listener on \"{}\" when removing", typeName, listener.getDevName()); // NOI18N 883 } 884 885 /** 886 * Get an I18N String of the Bean Listener type. 887 * @param listenerType the LISTENER_TYPE constant. 888 * @return I18N String of the Bean type, non-plural. 889 */ 890 private static String getListenerTypeName(int listenerType) { 891 String msg; 892 switch (listenerType) { 893 case LISTENER_TYPE_SENSOR: 894 msg = InstanceManager.getDefault(SensorManager.class).getBeanTypeHandled(); 895 break; 896 case LISTENER_TYPE_TURNOUT: 897 msg = InstanceManager.getDefault(TurnoutManager.class).getBeanTypeHandled(); 898 break; 899 case LISTENER_TYPE_LIGHT: 900 msg = InstanceManager.getDefault(LightManager.class).getBeanTypeHandled(); 901 break; 902 case LISTENER_TYPE_CONDITIONAL: 903 msg = InstanceManager.getDefault(ConditionalManager.class).getBeanTypeHandled(); 904 break; 905 case LISTENER_TYPE_SIGNALHEAD: 906 msg = InstanceManager.getDefault(SignalHeadManager.class).getBeanTypeHandled(); 907 break; 908 case LISTENER_TYPE_SIGNALMAST: 909 msg = InstanceManager.getDefault(SignalMastManager.class).getBeanTypeHandled(); 910 break; 911 case LISTENER_TYPE_MEMORY: 912 msg = InstanceManager.getDefault(MemoryManager.class).getBeanTypeHandled(); 913 break; 914 case LISTENER_TYPE_WARRANT: 915 msg = InstanceManager.getDefault(jmri.jmrit.logix.WarrantManager.class).getBeanTypeHandled(); 916 break; 917 case LISTENER_TYPE_OBLOCK: 918 msg = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getBeanTypeHandled(); 919 break; 920 case LISTENER_TYPE_ENTRYEXIT: 921 msg = InstanceManager.getDefault(EntryExitPairs.class).getBeanTypeHandled(); 922 break; 923 default: 924 msg = "unknown Listener type number: " + listenerType; // NOI18N 925 } 926 return msg; 927 } 928 929 /* /** 930 * Assembles a list of state variables that both trigger the Logix, and are 931 * changed by it. Returns true if any such variables were found. Returns 932 * false otherwise. Can be called when Logix is enabled. 933 * 934 * public boolean checkLoopCondition() { loopGremlins = new 935 * ArrayList<String[]>(); if (!_isActivated) { // Prepare a list of all 936 * variables used in conditionals java.util.HashSet <ConditionalVariable> 937 * variableList = new java.util.HashSet<ConditionalVariable>(); 938 * ConditionalManager cm = InstanceManager.getDefault(jmri.ConditionalManager.class); for 939 * (int i=0; i<_conditionalSystemNames.size(); i++) { Conditional c = null; 940 * c = cm.getBySystemName(_conditionalSystemNames.get(i)); if (c!=null) { // 941 * Not necesary to modify methods, equals and hashcode. Redundacy checked in 942 * addGremlin variableList.addAll(c.getCopyOfStateVariables()); } } 943 * java.util.HashSet <ConditionalVariable> variableList = new 944 * java.util.HashSet<ConditionalVariable>(); ConditionalVariable v = null; 945 * // check conditional action items Conditional c = null; for (int i=0; 946 * i<_conditionalSystemNames.size(); i++) { // get next conditional c = 947 * cm.getBySystemName(_conditionalSystemNames.get(i)); if (c!=null) { 948 * ArrayList <ConditionalAction> actionList = c.getCopyOfActions(); for (int 949 * j = 0; j < actionList.size(); j++) { ConditionalAction action = 950 * actionList.get(j); String sName = ""; String uName = ""; switch 951 * (action.getType()) { case Conditional.ACTION_NONE: break; case 952 * Conditional.ACTION_SET_TURNOUT: case Conditional.ACTION_DELAYED_TURNOUT: 953 * case Conditional.ACTION_RESET_DELAYED_TURNOUT: case 954 * Conditional.ACTION_CANCEL_TURNOUT_TIMERS: Turnout t = 955 * InstanceManager.turnoutManagerInstance(). 956 * provideTurnout(action.getDeviceName()); if (t!=null) { sName = 957 * t.getSystemName(); uName = t.getUserName(); // check for action on the 958 * same turnout Iterator <ConditionalVariable>it= variableList.iterator(); 959 * while(it.hasNext()) { v = it.next(); if (v.getType() == 960 * Conditional.TYPE_TURNOUT_CLOSED || v.getType() == 961 * Conditional.TYPE_TURNOUT_THROWN) { if ( (v.getName().equals(sName)) || 962 * (v.getName().equals(uName)) ) { // possible conflict found 963 * addGremlin("Turnout", sName, uName); } } } } break; case 964 * Conditional.ACTION_SET_SIGNAL_APPEARANCE: case 965 * Conditional.ACTION_SET_SIGNAL_HELD: case 966 * Conditional.ACTION_CLEAR_SIGNAL_HELD: case 967 * Conditional.ACTION_SET_SIGNAL_DARK: case 968 * Conditional.ACTION_SET_SIGNAL_LIT: SignalHead h = 969 * InstanceManager.getDefault(jmri.SignalHeadManager.class). 970 * getSignalHead(action.getDeviceName()); if (h!=null) { sName = 971 * h.getSystemName(); uName = h.getUserName(); // check for action on the 972 * same signal head Iterator <ConditionalVariable>it= 973 * variableList.iterator(); while(it.hasNext()) { v = it.next(); if 974 * (v.getType() >= Conditional.TYPE_SIGNAL_HEAD_RED || v.getType() <= 975 * Conditional.TYPE_SIGNAL_HEAD_HELD) { if ( (v.getName().equals(sName)) || 976 * (v.getName().equals(uName)) ) { // possible conflict found 977 * addGremlin("SignalHead", sName, uName); } } } } break; case 978 * Conditional.ACTION_SET_SENSOR: case Conditional.ACTION_DELAYED_SENSOR: 979 * case Conditional.ACTION_RESET_DELAYED_SENSOR: case 980 * Conditional.ACTION_CANCEL_SENSOR_TIMERS: Sensor s = 981 * InstanceManager.sensorManagerInstance(). 982 * provideSensor(action.getDeviceName()); if (s!=null) { sName = 983 * s.getSystemName(); uName = s.getUserName(); // check for action on the 984 * same sensor Iterator <ConditionalVariable>it= variableList.iterator(); 985 * while(it.hasNext()) { v = it.next(); if (v.getType() == 986 * Conditional.TYPE_SENSOR_ACTIVE || v.getType() == 987 * Conditional.TYPE_SENSOR_INACTIVE) { 988 * 989 * if ( (v.getName().equals(sName)) || (v.getName().equals(uName)) ) { // 990 * possible conflict found addGremlin("Sensor",sName, uName); } } } } break; 991 * case Conditional.ACTION_SET_LIGHT: case 992 * Conditional.ACTION_SET_LIGHT_TRANSITION_TIME: case 993 * Conditional.ACTION_SET_LIGHT_INTENSITY: Light lgt = 994 * InstanceManager.lightManagerInstance(). getLight(action.getDeviceName()); 995 * if (lgt!=null) { sName = lgt.getSystemName(); uName = lgt.getUserName(); 996 * // check for listener on the same light Iterator <ConditionalVariable>it= 997 * variableList.iterator(); while(it.hasNext()) { v = it.next(); if 998 * (v.getType() == Conditional.TYPE_LIGHT_ON || v.getType() == 999 * Conditional.TYPE_LIGHT_OFF) { if ( (v.getName().equals(sName)) || 1000 * (v.getName().equals(uName)) ) { // possible conflict found 1001 * addGremlin("Light", sName, uName); } } } } break; case 1002 * Conditional.ACTION_SET_MEMORY: case Conditional.ACTION_COPY_MEMORY: 1003 * Memory m = InstanceManager.memoryManagerInstance(). 1004 * provideMemory(action.getDeviceName()); if (m!=null) { sName = 1005 * m.getSystemName(); uName = m.getUserName(); // check for variable on the 1006 * same memory Iterator <ConditionalVariable>it= variableList.iterator(); 1007 * while(it.hasNext()) { v = it.next(); if (v.getType() == 1008 * Conditional.TYPE_MEMORY_EQUALS) { if ( (v.getName().equals(sName)) || 1009 * (v.getName().equals(uName)) ) { // possible conflict found 1010 * addGremlin("Memory", sName, uName); } } } } break; case 1011 * Conditional.ACTION_SET_FAST_CLOCK_TIME: case 1012 * Conditional.ACTION_START_FAST_CLOCK: case 1013 * Conditional.ACTION_STOP_FAST_CLOCK: Iterator <ConditionalVariable>it= 1014 * variableList.iterator(); while(it.hasNext()) { v = it.next(); if 1015 * (v.getType() == Conditional.TYPE_FAST_CLOCK_RANGE) { 1016 * addGremlin("FastClock", null, v.getName()); } } break; default: } } } } } 1017 * return (loopGremlins.size()>0); } 1018 * 1019 * private void addGremlin(String type, String sName, String uName) { // 1020 * check for redundancy String names = uName+ (sName == null ? "" : " 1021 * ("+sName+")"); for (int i=0; i<loopGremlins.size(); i++) { String[] str = 1022 * loopGremlins.get(i); if (str[0].equals(type) && str[1].equals(names)) { 1023 * return; } } String[] item = new String[2]; item[0] = type; item[1] = 1024 * names; loopGremlins.add(item); } 1025 * 1026 * ArrayList <String[]> loopGremlins = null; 1027 * 1028 * /** 1029 * Returns a string listing state variables that might result in a loop. 1030 * Returns an empty string if there are none, probably because 1031 * "checkLoopCondition" was not invoked before the call, or returned false. 1032 * 1033 * public ArrayList 1034 * <String[]> getLoopGremlins() {return(loopGremlins);} 1035 */ 1036 1037 /** 1038 * Not needed for Logixs - included to complete implementation of the 1039 * NamedBean interface. 1040 */ 1041 @Override 1042 public int getState() { 1043 log.warn("Unexpected call to getState in DefaultLogix."); // NOI18N 1044 return UNKNOWN; 1045 } 1046 1047 /** 1048 * Not needed for Logixs - included to complete implementation of the 1049 * NamedBean interface. 1050 * @param state unused. 1051 */ 1052 @Override 1053 public void setState(int state) { 1054 log.warn("Unexpected call to setState in DefaultLogix."); // NOI18N 1055 } 1056 1057 @Override 1058 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 1059 if (Manager.PROPERTY_CAN_DELETE.equals(evt.getPropertyName())) { 1060 NamedBean nb = (NamedBean) evt.getOldValue(); 1061 for (JmriSimplePropertyListener listener : _listeners) { 1062 if (nb.equals(listener.getBean())) { 1063 var e = new java.beans.PropertyChangeEvent(this, Manager.PROPERTY_DO_NOT_DELETE, null, null); 1064 throw new java.beans.PropertyVetoException( 1065 Bundle.getMessage("InUseLogixListener", nb.getBeanType(), getDisplayName()), e); // NOI18N 1066 } 1067 } 1068 1069 String cName; 1070 Conditional c; 1071 for (String conditionalSystemName : _conditionalSystemNames) { 1072 cName = conditionalSystemName; 1073 c = conditionalManager.getBySystemName(cName); 1074 if (c != null) { 1075 for (ConditionalAction ca : c.getCopyOfActions()) { 1076 if (nb.equals(ca.getBean())) { 1077 var e = new java.beans.PropertyChangeEvent( 1078 this, Manager.PROPERTY_DO_NOT_DELETE, null, null); 1079 throw new java.beans.PropertyVetoException( 1080 Bundle.getMessage("InUseLogixAction", nb.getBeanType(), getDisplayName()), e); // NOI18N 1081 } 1082 } 1083 for (ConditionalVariable v : c.getCopyOfStateVariables()) { 1084 if (nb.equals(v.getBean()) || nb.equals(v.getNamedBeanData())) { 1085 var e = new java.beans.PropertyChangeEvent(this, 1086 Manager.PROPERTY_DO_NOT_DELETE, null, null); 1087 throw new java.beans.PropertyVetoException( Bundle.getMessage("InUseLogixVariable", 1088 nb.getBeanType(), getDisplayName()), e); 1089 } 1090 } 1091 } 1092 } 1093 } 1094 } 1095 1096 @Override 1097 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 1098 List<NamedBeanUsageReport> report = new ArrayList<>(); 1099 if (bean != null) { 1100 for (int i = 0; i < getNumConditionals(); i++) { 1101 DefaultConditional cdl = (DefaultConditional) getConditional(getConditionalByNumberOrder(i)); 1102 if ( cdl == null ) { 1103 continue; 1104 } 1105 cdl.getStateVariableList().forEach( variable -> { 1106 if (bean.equals(variable.getBean())) { 1107 report.add(new NamedBeanUsageReport("ConditionalVariable", cdl, variable.toString())); 1108 } 1109 if (bean.equals(variable.getNamedBeanData())) { 1110 report.add(new NamedBeanUsageReport("ConditionalVariableData", cdl, variable.toString())); 1111 } 1112 }); 1113 cdl.getActionList().forEach( action -> { 1114 if (bean.equals(action.getBean())) { 1115 boolean triggerType = cdl.getTriggerOnChange(); 1116 report.add(new NamedBeanUsageReport("ConditionalAction", cdl, action.description(triggerType))); 1117 } 1118 }); 1119 } 1120 } 1121 return report; 1122 } 1123 1124 /** {@inheritDoc} */ 1125 @Override 1126 @OverridingMethodsMustInvokeSuper 1127 public void dispose() { 1128 // remove the listeners first, so a deleted Logix stops running 1129 deActivateLogix(); 1130 super.dispose(); 1131 for (int i = 0; i < getNumConditionals(); i++) { 1132 Conditional c = getConditional(getConditionalByNumberOrder(i)); 1133 if ( c != null ) { 1134 c.dispose(); 1135 } 1136 } 1137 } 1138 1139 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultLogix.class); 1140 1141}