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