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