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