001package jmri.jmrit.logixng.implementation;
002
003import java.beans.*;
004import java.io.PrintWriter;
005import java.util.*;
006
007import javax.annotation.Nonnull;
008
009import jmri.*;
010import jmri.jmrit.logixng.*;
011import jmri.jmrit.logixng.Module;
012import jmri.jmrit.logixng.SymbolTable.VariableData;
013import jmri.jmrit.logixng.actions.*;
014import jmri.jmrit.logixng.implementation.swing.ErrorHandlingDialog;
015import jmri.jmrit.logixng.implementation.swing.ErrorHandlingDialog_MultiLine;
016import jmri.jmrit.logixng.util.LogixNG_Thread;
017import jmri.util.LoggingUtil;
018import jmri.util.ThreadingUtil;
019
020import org.apache.commons.lang3.mutable.MutableInt;
021import org.slf4j.Logger;
022
023/**
024 * The abstract class that is the base class for all LogixNG classes that
025 * implements the Base interface.
026 *
027 * @author Daniel Bergqvist 2020
028 */
029public abstract class AbstractMaleSocket implements MaleSocket {
030
031    private final Base _object;
032    private boolean _locked = false;
033    private boolean _system = false;
034    protected final List<VariableData> _localVariables = new ArrayList<>();
035    private final BaseManager<? extends NamedBean> _manager;
036    private Base _parent;
037    private ErrorHandlingType _errorHandlingType = ErrorHandlingType.Default;
038    private boolean _catchAbortExecution;
039    private boolean _listen = true;     // By default, actions and expressions listen
040
041
042    private static class ErrorHandlingModuleClass {
043
044        private static final ErrorHandlingModuleClass INSTANCE = new ErrorHandlingModuleClass();
045
046        private final LogixNG errorHandlingLogixNG;
047        private final ConditionalNG errorHandlingConditionalNG;
048        private final Module errorHandlingModule;
049        private final Map<String, Object> _variablesWithValues;
050
051        private ErrorHandlingModuleClass() {
052            errorHandlingLogixNG = new DefaultLogixNG("IQ:JMRI:ErrorHandlingLogixNG", null);
053            errorHandlingConditionalNG = new DefaultConditionalNG("IQC:JMRI:ErrorHandlingCondtionalNG", null, LogixNG_Thread.ERROR_HANDLING_LOGIXNG_THREAD);
054            errorHandlingLogixNG.addConditionalNG(errorHandlingConditionalNG);
055
056            Module tempErrorHandlingModule = InstanceManager.getDefault(ModuleManager.class).getBySystemName(LogixNG_Manager.ERROR_HANDLING_MODULE_NAME);
057            if (tempErrorHandlingModule != null) {
058                errorHandlingModule = tempErrorHandlingModule;
059            } else {
060                FemaleSocketManager.SocketType socketType = InstanceManager.getDefault(
061                        FemaleSocketManager.class).getSocketTypeByType("DefaultFemaleDigitalActionSocket");
062                errorHandlingModule = new DefaultModule(LogixNG_Manager.ERROR_HANDLING_MODULE_NAME, null, socketType, false, false);
063                InstanceManager.getDefault(ModuleManager.class).register(errorHandlingModule);
064            }
065
066
067            DigitalMany many = new DigitalMany("IQDA:JMRI:ErrorHandlingAction", null);
068            MaleSocket maleSocketMany = new DefaultMaleDigitalActionSocket(
069                    InstanceManager.getDefault(DigitalActionManager.class), many);
070            many.setParent(maleSocketMany);
071
072            maleSocketMany.addLocalVariable("logixng", SymbolTable.InitialValueType.String, null);
073            maleSocketMany.addLocalVariable("conditionalng", SymbolTable.InitialValueType.String, null);
074            maleSocketMany.addLocalVariable("module", SymbolTable.InitialValueType.String, null);
075            maleSocketMany.addLocalVariable("item", SymbolTable.InitialValueType.String, null);
076            maleSocketMany.addLocalVariable("message", SymbolTable.InitialValueType.String, null);
077            maleSocketMany.addLocalVariable("messageList", SymbolTable.InitialValueType.String, null);
078            maleSocketMany.addLocalVariable("exception", SymbolTable.InitialValueType.String, null);
079
080            try {
081                errorHandlingConditionalNG.getFemaleSocket().connect(maleSocketMany);
082            } catch (SocketAlreadyConnectedException e) {
083                log.error("Exception when creating error handling LogixNG: ", e);
084            }
085
086            SetLocalVariables setLocalVariables = new SetLocalVariables("IQDA:JMRI:ErrorHandlingAction", null);
087            MaleSocket maleSocketSetLocalVariables = new DefaultMaleDigitalActionSocket(
088                    InstanceManager.getDefault(DigitalActionManager.class), setLocalVariables);
089            setLocalVariables.setParent(maleSocketSetLocalVariables);
090            _variablesWithValues = setLocalVariables.getMap();
091
092            try {
093                maleSocketMany.getChild(maleSocketMany.getChildCount()-1).connect(maleSocketSetLocalVariables);
094            } catch (SocketAlreadyConnectedException e) {
095                log.error("Exception when creating error handling LogixNG: ", e);
096            }
097
098            DigitalCallModule action = new DigitalCallModule("IQDA:JMRI:ErrorHandlingAction", null);
099            action.getSelectNamedBean().setNamedBean(errorHandlingModule);
100            action.addParameter("__logixng__", SymbolTable.InitialValueType.LocalVariable, "logixng", Module.ReturnValueType.None, null);
101            action.addParameter("__conditionalng__", SymbolTable.InitialValueType.LocalVariable, "conditionalng", Module.ReturnValueType.None, null);
102            action.addParameter("__module__", SymbolTable.InitialValueType.LocalVariable, "module", Module.ReturnValueType.None, null);
103            action.addParameter("__item__", SymbolTable.InitialValueType.LocalVariable, "item", Module.ReturnValueType.None, null);
104            action.addParameter("__message__", SymbolTable.InitialValueType.LocalVariable, "message", Module.ReturnValueType.None, null);
105            action.addParameter("__messageList__", SymbolTable.InitialValueType.LocalVariable, "messageList", Module.ReturnValueType.None, null);
106            action.addParameter("__exception__", SymbolTable.InitialValueType.LocalVariable, "exception", Module.ReturnValueType.None, null);
107            MaleSocket maleSocket = new DefaultMaleDigitalActionSocket(InstanceManager.getDefault(DigitalActionManager.class), action);
108            action.setParent(maleSocket);
109            try {
110                maleSocketMany.getChild(maleSocketMany.getChildCount()-1).connect(maleSocket);
111            } catch (SocketAlreadyConnectedException e) {
112                log.error("Exception when creating error handling LogixNG: ", e);
113            }
114            List<String> errors = new ArrayList<>();
115            errorHandlingLogixNG.setParentForAllChildren(errors);
116            if (!errors.isEmpty()) {
117                for (String s : errors) {
118                    log.error("Error: {}", s);
119                }
120            }
121        }
122    }
123
124
125    public AbstractMaleSocket(BaseManager<? extends NamedBean> manager, Base object) {
126        _manager = manager;
127        _object = object;
128    }
129
130    public static FemaleSocket getErrorHandlingModuleSocket() {
131        return ErrorHandlingModuleClass.INSTANCE.errorHandlingModule.getRootSocket();
132    }
133
134    public static boolean isErrorHandlingModuleEnabled() {
135        // Does the error handling module exist?
136        Module errorHandlingModule = InstanceManager.getDefault(ModuleManager.class)
137                .getBySystemName(LogixNG_Manager.ERROR_HANDLING_MODULE_NAME);
138        if (errorHandlingModule == null) {
139            return false;
140        }
141        return errorHandlingModule.getRootSocket().isConnected();
142    }
143
144    /** {@inheritDoc} */
145    @Override
146    public final Base getObject() {
147        return _object;
148    }
149
150    /** {@inheritDoc} */
151    @Override
152    public final Base getRoot() {
153        return _object.getRoot();
154    }
155
156    /** {@inheritDoc} */
157    @Override
158    public boolean isLocked() {
159        if (_object instanceof MaleSocket) {
160            return ((MaleSocket)_object).isLocked();
161        }
162        return _locked;
163    }
164
165    /** {@inheritDoc} */
166    @Override
167    public void setLocked(boolean locked) {
168        if (_object instanceof MaleSocket) {
169            ((MaleSocket)_object).setLocked(locked);
170        }
171        _locked = locked;
172    }
173
174    /** {@inheritDoc} */
175    @Override
176    public boolean isSystem() {
177        if (_object instanceof MaleSocket) {
178            return ((MaleSocket)_object).isSystem();
179        }
180        return _system;
181    }
182
183    /** {@inheritDoc} */
184    @Override
185    public void setSystem(boolean system) {
186        if (_object instanceof MaleSocket) {
187            ((MaleSocket)_object).setSystem(system);
188        }
189        _system = system;
190    }
191
192    /** {@inheritDoc} */
193    @Override
194    public final Category getCategory() {
195        return _object.getCategory();
196    }
197
198    @Override
199    public final FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
200        return _object.getChild(index);
201    }
202
203    @Override
204    public final int getChildCount() {
205        return _object.getChildCount();
206    }
207
208    @Override
209    public final String getShortDescription(Locale locale) {
210        return _object.getShortDescription(locale);
211    }
212
213    @Override
214    public final String getLongDescription(Locale locale) {
215        String s = _object.getLongDescription(locale);
216        if (!_listen) {
217            s += " ::: " + Base.getNoListenString();
218        }
219        return s;
220    }
221
222    @Override
223    public final String getUserName() {
224        return _object.getUserName();
225    }
226
227    @Override
228    public final void setUserName(String s) throws NamedBean.BadUserNameException {
229        _object.setUserName(s);
230    }
231
232    @Override
233    public final String getSystemName() {
234        return _object.getSystemName();
235    }
236
237    @Override
238    public final void addPropertyChangeListener(PropertyChangeListener l, String name, String listenerRef) {
239        _object.addPropertyChangeListener(l, name, listenerRef);
240    }
241
242    @Override
243    public final void addPropertyChangeListener(String propertyName, PropertyChangeListener l, String name, String listenerRef) {
244        _object.addPropertyChangeListener(propertyName, l, name, listenerRef);
245    }
246
247    @Override
248    public final void addPropertyChangeListener(PropertyChangeListener l) {
249        _object.addPropertyChangeListener(l);
250    }
251
252    @Override
253    public final void addPropertyChangeListener(String propertyName, PropertyChangeListener l) {
254        _object.addPropertyChangeListener(propertyName, l);
255    }
256
257    @Override
258    public final void removePropertyChangeListener(PropertyChangeListener l) {
259        _object.removePropertyChangeListener(l);
260    }
261
262    @Override
263    public final void removePropertyChangeListener(String propertyName, PropertyChangeListener l) {
264        _object.removePropertyChangeListener(propertyName, l);
265    }
266
267    @Override
268    public final void updateListenerRef(PropertyChangeListener l, String newName) {
269        _object.updateListenerRef(l, newName);
270    }
271
272    @Override
273    public final void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
274        _object.vetoableChange(evt);
275    }
276
277    @Override
278    public final String getListenerRef(PropertyChangeListener l) {
279        return _object.getListenerRef(l);
280    }
281
282    @Override
283    public final ArrayList<String> getListenerRefs() {
284        return _object.getListenerRefs();
285    }
286
287    @Override
288    public final int getNumPropertyChangeListeners() {
289        return _object.getNumPropertyChangeListeners();
290    }
291
292    @Override
293    public final synchronized PropertyChangeListener[] getPropertyChangeListeners() {
294        return _object.getPropertyChangeListeners();
295    }
296
297    @Override
298    public final synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
299        return _object.getPropertyChangeListeners(propertyName);
300    }
301
302    @Override
303    public final PropertyChangeListener[] getPropertyChangeListenersByReference(String name) {
304        return _object.getPropertyChangeListenersByReference(name);
305    }
306
307    @Override
308    public String getComment() {
309        return _object.getComment();
310    }
311
312    @Override
313    public void setComment(String comment) {
314        _object.setComment(comment);
315    }
316
317    @Override
318    public boolean getListen() {
319        if (getObject() instanceof MaleSocket) {
320            return ((MaleSocket)getObject()).getListen();
321        }
322        return _listen;
323    }
324
325    @Override
326    public void setListen(boolean listen)
327    {
328        if (getObject() instanceof MaleSocket) {
329            ((MaleSocket)getObject()).setListen(listen);
330        }
331        _listen = listen;
332    }
333
334    @Override
335    public boolean getCatchAbortExecution() {
336        return _catchAbortExecution;
337    }
338
339    @Override
340    public void setCatchAbortExecution(boolean catchAbortExecution)
341    {
342        _catchAbortExecution = catchAbortExecution;
343    }
344
345    @Override
346    public void addLocalVariable(
347            String name,
348            SymbolTable.InitialValueType initialValueType,
349            String initialValueData) {
350
351        if (getObject() instanceof MaleSocket) {
352            ((MaleSocket)getObject()).addLocalVariable(name, initialValueType, initialValueData);
353        } else {
354            _localVariables.add(new VariableData(name, initialValueType, initialValueData));
355        }
356    }
357
358    @Override
359    public void addLocalVariable(VariableData variableData) {
360
361        if (getObject() instanceof MaleSocket) {
362            ((MaleSocket)getObject()).addLocalVariable(variableData);
363        } else {
364            _localVariables.add(variableData);
365        }
366    }
367
368    @Override
369    public void clearLocalVariables() {
370        if (getObject() instanceof MaleSocket) {
371            ((MaleSocket)getObject()).clearLocalVariables();
372        } else {
373            _localVariables.clear();
374        }
375    }
376
377    @Override
378    public List<VariableData> getLocalVariables() {
379        if (getObject() instanceof MaleSocket) {
380            return ((MaleSocket)getObject()).getLocalVariables();
381        } else {
382            return _localVariables;
383        }
384    }
385
386    @Override
387    public Base getParent() {
388        return _parent;
389    }
390
391    @Override
392    public void setParent(Base parent) {
393        _parent = parent;
394    }
395
396    @Override
397    public final ConditionalNG getConditionalNG() {
398        if (getParent() == null) return null;
399        return getParent().getConditionalNG();
400    }
401
402    @Override
403    public final LogixNG getLogixNG() {
404        if (getParent() == null) return null;
405        return getParent().getLogixNG();
406    }
407
408    /** {@inheritDoc} */
409    @Override
410    public final boolean setParentForAllChildren(List<String> errors) {
411        boolean result = true;
412        for (int i=0; i < getChildCount(); i++) {
413            FemaleSocket femaleSocket = getChild(i);
414            if (femaleSocket.isConnected()) {
415                MaleSocket connectedSocket = femaleSocket.getConnectedSocket();
416                connectedSocket.setParent(femaleSocket);
417                result = result && connectedSocket.setParentForAllChildren(errors);
418            }
419        }
420        return result;
421    }
422
423    /**
424     * Register listeners if this object needs that.
425     * <P>
426     * Important: This method may be called more than once. Methods overriding
427     * this method must ensure that listeners are not registered more than once.
428     */
429    abstract protected void registerListenersForThisClass();
430
431    /**
432     * Unregister listeners if this object needs that.
433     * <P>
434     * Important: This method may be called more than once. Methods overriding
435     * this method must ensure that listeners are not unregistered more than once.
436     */
437    abstract protected void unregisterListenersForThisClass();
438
439    /** {@inheritDoc} */
440    @Override
441    public final void registerListeners() {
442        if (getObject() instanceof MaleSocket) {
443            getObject().registerListeners();
444        } else {
445            if (_listen) {
446                registerListenersForThisClass();
447                for (int i=0; i < getChildCount(); i++) {
448                    getChild(i).registerListeners();
449                }
450            }
451        }
452    }
453
454    /** {@inheritDoc} */
455    @Override
456    public final void unregisterListeners() {
457        if (getObject() instanceof MaleSocket) {
458            getObject().unregisterListeners();
459        } else {
460            unregisterListenersForThisClass();
461            for (int i=0; i < getChildCount(); i++) {
462                getChild(i).unregisterListeners();
463            }
464        }
465    }
466
467    /** {@inheritDoc} */
468    @Override
469    public final boolean isActive() {
470        return isEnabled() && ((getParent() == null) || getParent().isActive());
471    }
472
473    /**
474     * Print this row.
475     * If getObject() doesn't return an AbstractMaleSocket, print this row.
476     * <P>
477     * If a male socket that extends AbstractMaleSocket wants to print
478     * something here, it needs to override this method.
479     * <P>
480     * The reason this method doesn't print if getObject() returns an
481     * AbstractMaleSocket is to protect so it doesn't print itself twice if
482     * it's embedding an other AbstractMaleSocket. An example of this is the
483     * AbstractDebuggerMaleSocket which embeds other male sockets.
484     *
485     * @param settings settings for what to print
486     * @param locale The locale to be used
487     * @param writer the stream to print the tree to
488     * @param currentIndent the current indentation
489     * @param lineNumber the line number
490     */
491    protected void printTreeRow(
492            PrintTreeSettings settings,
493            Locale locale,
494            PrintWriter writer,
495            String currentIndent,
496            MutableInt lineNumber) {
497
498        if (!(getObject() instanceof AbstractMaleSocket)) {
499            String comment = getComment();
500            if (comment != null) {
501                comment = comment.replaceAll("\\r\\n", "\\n");
502                comment = comment.replaceAll("\\r", "\\n");
503                for (String s : comment.split("\\n", 0)) {
504                    if (settings._printLineNumbers) {
505                        writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1)));
506                    }
507                    writer.append(currentIndent);
508                    writer.append("// ");
509                    writer.append(s);
510                    writer.println();
511                }
512            }
513            if (settings._printLineNumbers) {
514                writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1)));
515            }
516            writer.append(currentIndent);
517            writer.append(getLongDescription(locale));
518            if (settings._printSystemNames) {
519                writer.append(" ::: ");
520                writer.append(this.getSystemName());
521            }
522            if (settings._printDisplayName) {
523                writer.append(" ::: ");
524                writer.append(Bundle.getMessage("LabelDisplayName"));
525                writer.append(" ");
526                writer.append(((NamedBean)this).getDisplayName(
527                        NamedBean.DisplayOptions.USERNAME_SYSTEMNAME));
528            } else if (!settings._hideUserName && getUserName() != null) {
529                writer.append(" ::: ");
530                writer.append(Bundle.getMessage("LabelUserName"));
531                writer.append(" ");
532                writer.append(getUserName());
533            }
534
535            if (settings._printErrorHandling) {
536                writer.append(" ::: ");
537                writer.append(getErrorHandlingType().toString());
538            }
539            if (!isEnabled()) {
540                writer.append(" ::: ");
541                writer.append(Bundle.getMessage("AbstractMaleSocket_Disabled"));
542            }
543            if (isLocked()) {
544                writer.append(" ::: ");
545                writer.append(Bundle.getMessage("AbstractMaleSocket_Locked"));
546            }
547            if (isSystem()) {
548                writer.append(" ::: ");
549                writer.append(Bundle.getMessage("AbstractMaleSocket_System"));
550            }
551            writer.println();
552        }
553    }
554
555    protected void printLocalVariable(
556            PrintTreeSettings settings,
557            Locale locale,
558            PrintWriter writer,
559            String currentIndent,
560            MutableInt lineNumber,
561            VariableData localVariable) {
562
563        if (settings._printLineNumbers) {
564            writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1)));
565        }
566        writer.append(currentIndent);
567        writer.append("   ::: ");
568        writer.append(Bundle.getMessage(
569                locale,
570                "PrintLocalVariable",
571                localVariable._name,
572                localVariable._initialValueType.toString(),
573                localVariable._initialValueData));
574        writer.println();
575    }
576
577    /** {@inheritDoc} */
578    @Override
579    public void printTree(
580            PrintTreeSettings settings,
581            PrintWriter writer,
582            String indent,
583            MutableInt lineNumber) {
584        printTree(settings, Locale.getDefault(), writer, indent, "", lineNumber);
585    }
586
587    /** {@inheritDoc} */
588    @Override
589    public void printTree(
590            PrintTreeSettings settings,
591            Locale locale,
592            PrintWriter writer,
593            String indent,
594            MutableInt lineNumber) {
595        printTree(settings, locale, writer, indent, "", lineNumber);
596    }
597
598    /** {@inheritDoc} */
599    @Override
600    public void printTree(
601            PrintTreeSettings settings,
602            Locale locale,
603            PrintWriter writer,
604            String indent,
605            String currentIndent,
606            MutableInt lineNumber) {
607
608        printTreeRow(settings, locale, writer, currentIndent, lineNumber);
609
610        if (settings._printLocalVariables) {
611            for (VariableData localVariable : _localVariables) {
612                printLocalVariable(settings, locale, writer, currentIndent, lineNumber, localVariable);
613            }
614        }
615
616        if (getObject() instanceof MaleSocket) {
617            getObject().printTree(settings, locale, writer, indent, currentIndent, lineNumber);
618        } else {
619            for (int i=0; i < getChildCount(); i++) {
620                getChild(i).printTree(settings, locale, writer, indent, currentIndent+indent, lineNumber);
621            }
622        }
623    }
624
625    /** {@inheritDoc} */
626    @Override
627    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
628                                                        justification="Specific log message format")
629    public void getUsageTree(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
630        if (!(getObject() instanceof AbstractMaleSocket)) {
631            log.debug("*@ {} :: {}", level, this.getLongDescription());
632            _object.getUsageDetail(level, bean, report, cdl);
633        }
634
635        if (getObject() instanceof MaleSocket) {
636            getObject().getUsageTree(level, bean, report, cdl);
637        } else {
638            level++;
639            for (int i=0; i < getChildCount(); i++) {
640                getChild(i).getUsageTree(level, bean, report, cdl);
641            }
642        }
643    }
644
645    /** {@inheritDoc} */
646    @Override
647    public void getUsageDetail(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) {
648    }
649
650    @Override
651    public BaseManager<? extends NamedBean> getManager() {
652        return _manager;
653    }
654
655    @Override
656    public final Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames)
657            throws JmriException {
658
659        MaleSocket maleSocket = (MaleSocket)getObject().getDeepCopy(systemNames, userNames);
660
661        maleSocket.setComment(this.getComment());
662        if (maleSocket.getDebugConfig() != null) {
663            maleSocket.setDebugConfig(maleSocket.getDebugConfig().getCopy());
664        }
665        maleSocket.setEnabledFlag(isEnabled());
666        maleSocket.setListen(getListen());
667        maleSocket.setErrorHandlingType(getErrorHandlingType());
668        maleSocket.setLocked(isLocked());
669        maleSocket.setSystem(false);    // If a system item is copied, the new item is not treated as system
670        maleSocket.setCatchAbortExecution(getCatchAbortExecution());
671
672        for (VariableData data : _localVariables) {
673            maleSocket.addLocalVariable(data._name, data._initialValueType, data._initialValueData);
674        }
675
676        return maleSocket;
677    }
678
679    @Override
680    public final Base deepCopyChildren(Base original, Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
681        getObject().deepCopyChildren(original, systemNames, userNames);
682        return this;
683    }
684
685    /**
686     * Disposes this object.
687     * This must remove _all_ connections!
688     */
689    abstract protected void disposeMe();
690
691    /** {@inheritDoc} */
692    @Override
693    public final void dispose() {
694        for (int i=0; i < getChildCount(); i++) {
695            getChild(i).dispose();
696        }
697        disposeMe();
698    }
699
700    @Override
701    public ErrorHandlingType getErrorHandlingType() {
702        if (getObject() instanceof MaleSocket) {
703            return ((MaleSocket)getObject()).getErrorHandlingType();
704        } else {
705            return _errorHandlingType;
706        }
707    }
708
709    @Override
710    public void setErrorHandlingType(ErrorHandlingType errorHandlingType)
711    {
712        if (getObject() instanceof MaleSocket) {
713            ((MaleSocket)getObject()).setErrorHandlingType(errorHandlingType);
714        } else {
715            _errorHandlingType = errorHandlingType;
716        }
717    }
718
719    /**
720     * Executes the error handling module.
721     * @param  item           the item that had the error
722     * @param  message        the error message
723     * @param  messageList    a list of error messages
724     * @param  e              the exception that has happened
725     */
726    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings({"SLF4J_SIGN_ONLY_FORMAT"})
727            // justification="The message is on several lines")
728    public void executeErrorHandlingModule(
729            Base item,
730            String message,
731            List<String> messageList,
732            Exception e) {
733
734        // Don't call the error handling module if it doesnt exist.
735        Module errorHandlingModule = InstanceManager.getDefault(ModuleManager.class)
736                .getBySystemName(LogixNG_Manager.ERROR_HANDLING_MODULE_NAME);
737        if (errorHandlingModule == null) {
738            return;
739        }
740
741        // Don't call the error handling module if it hasn't any children.
742        if (!ErrorHandlingModuleClass.INSTANCE.errorHandlingModule.getRootSocket().isConnected()) {
743            return;
744        }
745
746        // Don't call the error handling module recursively. It would happen
747        // if there is an error in the error handling module.
748        if (item.getModule() == ErrorHandlingModuleClass.INSTANCE.errorHandlingModule) {
749            log.warn("Exception in LogixNG error handling module. Can't execute error handling module recursively.");
750            if (messageList != null) {
751                for (String s : messageList) {
752                    log.warn("   {}", s);
753                }
754            } else {
755                log.warn("   {}", message);
756            }
757            log.warn("Exception: ", e);
758        }
759
760        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("logixng", item.getLogixNG());
761        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("conditionalng", item.getConditionalNG());
762        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("module", item.getModule());
763        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("item", item);
764        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("message", message);
765        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("messageList", messageList);
766        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("exception", e);
767        ErrorHandlingModuleClass.INSTANCE.errorHandlingConditionalNG.execute();
768    }
769
770    @Override
771    public void handleError(Base item, String message, JmriException e, Logger log) throws JmriException {
772
773        // Always throw AbortConditionalNGExecutionException exceptions
774        if (!_catchAbortExecution && (e instanceof AbortConditionalNGExecutionException)) throw e;
775
776        ErrorHandlingType errorHandlingType = getErrorHandlingType();
777        if (errorHandlingType == ErrorHandlingType.Default) {
778            errorHandlingType = InstanceManager.getDefault(LogixNGPreferences.class)
779                    .getErrorHandlingType();
780        }
781
782        executeErrorHandlingModule(item, message, null, e);
783
784        switch (errorHandlingType) {
785            case ShowDialogBox:
786                boolean abort = ThreadingUtil.runOnGUIwithReturn(() -> {
787                    ErrorHandlingDialog dialog = new ErrorHandlingDialog();
788                    return dialog.showDialog(item, message);
789                });
790                if (abort) throw new AbortConditionalNGExecutionException(this, e);
791                break;
792
793            case LogError:
794                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
795                break;
796
797            case LogErrorOnce:
798                LoggingUtil.warnOnce(log, "item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
799                break;
800
801            case ThrowException:
802                throw e;
803
804            case AbortExecution:
805                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
806                throw new AbortConditionalNGExecutionException(this, e);
807
808            case AbortWithoutError:
809                throw new AbortConditionalNG_IgnoreException(this, e);
810
811            default:
812                throw e;
813        }
814    }
815
816    @Override
817    public void handleError(
818            Base item,
819            String message,
820            List<String> messageList,
821            JmriException e,
822            Logger log)
823            throws JmriException {
824
825        ErrorHandlingType errorHandlingType = getErrorHandlingType();
826        if (errorHandlingType == ErrorHandlingType.Default) {
827            errorHandlingType = InstanceManager.getDefault(LogixNGPreferences.class)
828                    .getErrorHandlingType();
829        }
830
831        executeErrorHandlingModule(item, message, messageList, e);
832
833        switch (errorHandlingType) {
834            case ShowDialogBox:
835                boolean abort = ThreadingUtil.runOnGUIwithReturn(() -> {
836                    ErrorHandlingDialog_MultiLine dialog = new ErrorHandlingDialog_MultiLine();
837                    return dialog.showDialog(item, message, messageList);
838                });
839                if (abort) throw new AbortConditionalNGExecutionException(this, e);
840                break;
841
842            case LogError:
843                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
844                break;
845
846            case LogErrorOnce:
847                LoggingUtil.warnOnce(log, "item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
848                break;
849
850            case ThrowException:
851                throw e;
852
853            case AbortExecution:
854                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
855                throw new AbortConditionalNGExecutionException(this, e);
856
857            case AbortWithoutError:
858                throw new AbortConditionalNG_IgnoreException(this, e);
859
860            default:
861                throw e;
862        }
863    }
864
865    @Override
866    public void handleError(Base item, String message, RuntimeException e, Logger log) throws JmriException {
867
868        ErrorHandlingType errorHandlingType = getErrorHandlingType();
869        if (errorHandlingType == ErrorHandlingType.Default) {
870            errorHandlingType = InstanceManager.getDefault(LogixNGPreferences.class)
871                    .getErrorHandlingType();
872        }
873
874        executeErrorHandlingModule(item, message, null, e);
875
876        switch (errorHandlingType) {
877            case ShowDialogBox:
878                boolean abort = ThreadingUtil.runOnGUIwithReturn(() -> {
879                    ErrorHandlingDialog dialog = new ErrorHandlingDialog();
880                    return dialog.showDialog(item, message);
881                });
882                if (abort) throw new AbortConditionalNGExecutionException(this, e);
883                break;
884
885            case LogError:
886//                e.printStackTrace();
887                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
888                break;
889
890            case LogErrorOnce:
891//                e.printStackTrace();
892                LoggingUtil.warnOnce(log, "item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
893                break;
894
895            case ThrowException:
896                throw e;
897
898            case AbortExecution:
899                throw new AbortConditionalNGExecutionException(this, e);
900
901            case AbortWithoutError:
902                throw new AbortConditionalNG_IgnoreException(this, e);
903
904            default:
905                throw e;
906        }
907    }
908
909    /** {@inheritDoc} */
910    @Override
911    public void getListenerRefsIncludingChildren(List<String> list) {
912        list.addAll(getListenerRefs());
913        for (int i=0; i < getChildCount(); i++) {
914            getChild(i).getListenerRefsIncludingChildren(list);
915        }
916    }
917
918    @Override
919    public boolean hasChild(@Nonnull Base b) {
920        return getObject() == b;
921    }
922
923    /** {@inheritDoc} */
924    @Override
925    public String toString() {
926        return getObject().toString();
927    }
928
929    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractMaleSocket.class);
930}