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        // Only the innermost male socket should add "::: Listen"
217        if (!_listen && !(getObject() instanceof MaleSocket)) {
218            s += " ::: " + Base.getNoListenString();
219        }
220        return s;
221    }
222
223    @Override
224    public final String getUserName() {
225        return _object.getUserName();
226    }
227
228    @Override
229    public final void setUserName(String s) throws NamedBean.BadUserNameException {
230        _object.setUserName(s);
231    }
232
233    @Override
234    public final String getSystemName() {
235        return _object.getSystemName();
236    }
237
238    @Override
239    public final void addPropertyChangeListener(PropertyChangeListener l, String name, String listenerRef) {
240        _object.addPropertyChangeListener(l, name, listenerRef);
241    }
242
243    @Override
244    public final void addPropertyChangeListener(String propertyName, PropertyChangeListener l, String name, String listenerRef) {
245        _object.addPropertyChangeListener(propertyName, l, name, listenerRef);
246    }
247
248    @Override
249    public final void addPropertyChangeListener(PropertyChangeListener l) {
250        _object.addPropertyChangeListener(l);
251    }
252
253    @Override
254    public final void addPropertyChangeListener(String propertyName, PropertyChangeListener l) {
255        _object.addPropertyChangeListener(propertyName, l);
256    }
257
258    @Override
259    public final void removePropertyChangeListener(PropertyChangeListener l) {
260        _object.removePropertyChangeListener(l);
261    }
262
263    @Override
264    public final void removePropertyChangeListener(String propertyName, PropertyChangeListener l) {
265        _object.removePropertyChangeListener(propertyName, l);
266    }
267
268    @Override
269    public final void updateListenerRef(PropertyChangeListener l, String newName) {
270        _object.updateListenerRef(l, newName);
271    }
272
273    @Override
274    public final void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
275        _object.vetoableChange(evt);
276    }
277
278    @Override
279    public final String getListenerRef(PropertyChangeListener l) {
280        return _object.getListenerRef(l);
281    }
282
283    @Override
284    public final ArrayList<String> getListenerRefs() {
285        return _object.getListenerRefs();
286    }
287
288    @Override
289    public final int getNumPropertyChangeListeners() {
290        return _object.getNumPropertyChangeListeners();
291    }
292
293    @Override
294    public final synchronized PropertyChangeListener[] getPropertyChangeListeners() {
295        return _object.getPropertyChangeListeners();
296    }
297
298    @Override
299    public final synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
300        return _object.getPropertyChangeListeners(propertyName);
301    }
302
303    @Override
304    public final PropertyChangeListener[] getPropertyChangeListenersByReference(String name) {
305        return _object.getPropertyChangeListenersByReference(name);
306    }
307
308    @Override
309    public String getComment() {
310        return _object.getComment();
311    }
312
313    @Override
314    public void setComment(String comment) {
315        _object.setComment(comment);
316    }
317
318    @Override
319    public boolean getListen() {
320        if (getObject() instanceof MaleSocket) {
321            return ((MaleSocket)getObject()).getListen();
322        }
323        return _listen;
324    }
325
326    @Override
327    public void setListen(boolean listen)
328    {
329        if (getObject() instanceof MaleSocket) {
330            ((MaleSocket)getObject()).setListen(listen);
331        }
332        _listen = listen;
333    }
334
335    @Override
336    public boolean getCatchAbortExecution() {
337        return _catchAbortExecution;
338    }
339
340    @Override
341    public void setCatchAbortExecution(boolean catchAbortExecution)
342    {
343        _catchAbortExecution = catchAbortExecution;
344    }
345
346    @Override
347    public void addLocalVariable(
348            String name,
349            SymbolTable.InitialValueType initialValueType,
350            String initialValueData) {
351
352        if (getObject() instanceof MaleSocket) {
353            ((MaleSocket)getObject()).addLocalVariable(name, initialValueType, initialValueData);
354        } else {
355            _localVariables.add(new VariableData(name, initialValueType, initialValueData));
356        }
357    }
358
359    @Override
360    public void addLocalVariable(VariableData variableData) {
361
362        if (getObject() instanceof MaleSocket) {
363            ((MaleSocket)getObject()).addLocalVariable(variableData);
364        } else {
365            _localVariables.add(variableData);
366        }
367    }
368
369    @Override
370    public void clearLocalVariables() {
371        if (getObject() instanceof MaleSocket) {
372            ((MaleSocket)getObject()).clearLocalVariables();
373        } else {
374            _localVariables.clear();
375        }
376    }
377
378    @Override
379    public List<VariableData> getLocalVariables() {
380        if (getObject() instanceof MaleSocket) {
381            return ((MaleSocket)getObject()).getLocalVariables();
382        } else {
383            return _localVariables;
384        }
385    }
386
387    @Override
388    public Base getParent() {
389        return _parent;
390    }
391
392    @Override
393    public void setParent(Base parent) {
394        _parent = parent;
395    }
396
397    @Override
398    public final ConditionalNG getConditionalNG() {
399        if (getParent() == null) return null;
400        return getParent().getConditionalNG();
401    }
402
403    @Override
404    public final LogixNG getLogixNG() {
405        if (getParent() == null) return null;
406        return getParent().getLogixNG();
407    }
408
409    /** {@inheritDoc} */
410    @Override
411    public final boolean setParentForAllChildren(List<String> errors) {
412        boolean result = true;
413        for (int i=0; i < getChildCount(); i++) {
414            FemaleSocket femaleSocket = getChild(i);
415            if (femaleSocket.isConnected()) {
416                MaleSocket connectedSocket = femaleSocket.getConnectedSocket();
417                connectedSocket.setParent(femaleSocket);
418                result = result && connectedSocket.setParentForAllChildren(errors);
419            }
420        }
421        return result;
422    }
423
424    /**
425     * Register listeners if this object needs that.
426     * <P>
427     * Important: This method may be called more than once. Methods overriding
428     * this method must ensure that listeners are not registered more than once.
429     */
430    abstract protected void registerListenersForThisClass();
431
432    /**
433     * Unregister listeners if this object needs that.
434     * <P>
435     * Important: This method may be called more than once. Methods overriding
436     * this method must ensure that listeners are not unregistered more than once.
437     */
438    abstract protected void unregisterListenersForThisClass();
439
440    /** {@inheritDoc} */
441    @Override
442    public final void registerListeners() {
443        if (getObject() instanceof MaleSocket) {
444            getObject().registerListeners();
445        } else {
446            if (_listen) {
447                registerListenersForThisClass();
448                for (int i=0; i < getChildCount(); i++) {
449                    getChild(i).registerListeners();
450                }
451            }
452        }
453    }
454
455    /** {@inheritDoc} */
456    @Override
457    public final void unregisterListeners() {
458        if (getObject() instanceof MaleSocket) {
459            getObject().unregisterListeners();
460        } else {
461            unregisterListenersForThisClass();
462            for (int i=0; i < getChildCount(); i++) {
463                getChild(i).unregisterListeners();
464            }
465        }
466    }
467
468    /** {@inheritDoc} */
469    @Override
470    public final boolean isActive() {
471        return isEnabled() && ((getParent() == null) || getParent().isActive());
472    }
473
474    /**
475     * Print this row.
476     * If getObject() doesn't return an AbstractMaleSocket, print this row.
477     * <P>
478     * If a male socket that extends AbstractMaleSocket wants to print
479     * something here, it needs to override this method.
480     * <P>
481     * The reason this method doesn't print if getObject() returns an
482     * AbstractMaleSocket is to protect so it doesn't print itself twice if
483     * it's embedding an other AbstractMaleSocket. An example of this is the
484     * AbstractDebuggerMaleSocket which embeds other male sockets.
485     *
486     * @param settings settings for what to print
487     * @param locale The locale to be used
488     * @param writer the stream to print the tree to
489     * @param currentIndent the current indentation
490     * @param lineNumber the line number
491     */
492    protected void printTreeRow(
493            PrintTreeSettings settings,
494            Locale locale,
495            PrintWriter writer,
496            String currentIndent,
497            MutableInt lineNumber) {
498
499        if (!(getObject() instanceof AbstractMaleSocket)) {
500            String comment = getComment();
501            if (comment != null) {
502                comment = comment.replaceAll("\\r\\n", "\\n");
503                comment = comment.replaceAll("\\r", "\\n");
504                for (String s : comment.split("\\n", 0)) {
505                    if (settings._printLineNumbers) {
506                        writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1)));
507                    }
508                    writer.append(currentIndent);
509                    writer.append("// ");
510                    writer.append(s);
511                    writer.println();
512                }
513            }
514            if (settings._printLineNumbers) {
515                writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1)));
516            }
517            writer.append(currentIndent);
518            writer.append(getLongDescription(locale));
519            if (settings._printSystemNames) {
520                writer.append(" ::: ");
521                writer.append(this.getSystemName());
522            }
523            if (settings._printDisplayName) {
524                writer.append(" ::: ");
525                writer.append(Bundle.getMessage("LabelDisplayName"));
526                writer.append(" ");
527                writer.append(((NamedBean)this).getDisplayName(
528                        NamedBean.DisplayOptions.USERNAME_SYSTEMNAME));
529            } else if (!settings._hideUserName && getUserName() != null) {
530                writer.append(" ::: ");
531                writer.append(Bundle.getMessage("LabelUserName"));
532                writer.append(" ");
533                writer.append(getUserName());
534            }
535
536            if (settings._printErrorHandling) {
537                writer.append(" ::: ");
538                writer.append(getErrorHandlingType().toString());
539            }
540            if (!isEnabled()) {
541                writer.append(" ::: ");
542                writer.append(Bundle.getMessage("AbstractMaleSocket_Disabled"));
543            }
544            if (isLocked()) {
545                writer.append(" ::: ");
546                writer.append(Bundle.getMessage("AbstractMaleSocket_Locked"));
547            }
548            if (isSystem()) {
549                writer.append(" ::: ");
550                writer.append(Bundle.getMessage("AbstractMaleSocket_System"));
551            }
552            writer.println();
553        }
554    }
555
556    protected void printLocalVariable(
557            PrintTreeSettings settings,
558            Locale locale,
559            PrintWriter writer,
560            String currentIndent,
561            MutableInt lineNumber,
562            VariableData localVariable) {
563
564        if (settings._printLineNumbers) {
565            writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1)));
566        }
567        writer.append(currentIndent);
568        writer.append("   ::: ");
569        writer.append(Bundle.getMessage(
570                locale,
571                "PrintLocalVariable",
572                localVariable._name,
573                localVariable._initialValueType.toString(),
574                localVariable._initialValueData));
575        writer.println();
576    }
577
578    /** {@inheritDoc} */
579    @Override
580    public void printTree(
581            PrintTreeSettings settings,
582            PrintWriter writer,
583            String indent,
584            MutableInt lineNumber) {
585        printTree(settings, Locale.getDefault(), writer, indent, "", lineNumber);
586    }
587
588    /** {@inheritDoc} */
589    @Override
590    public void printTree(
591            PrintTreeSettings settings,
592            Locale locale,
593            PrintWriter writer,
594            String indent,
595            MutableInt lineNumber) {
596        printTree(settings, locale, writer, indent, "", lineNumber);
597    }
598
599    /** {@inheritDoc} */
600    @Override
601    public void printTree(
602            PrintTreeSettings settings,
603            Locale locale,
604            PrintWriter writer,
605            String indent,
606            String currentIndent,
607            MutableInt lineNumber) {
608
609        printTreeRow(settings, locale, writer, currentIndent, lineNumber);
610
611        if (settings._printLocalVariables) {
612            for (VariableData localVariable : _localVariables) {
613                printLocalVariable(settings, locale, writer, currentIndent, lineNumber, localVariable);
614            }
615        }
616
617        if (getObject() instanceof MaleSocket) {
618            getObject().printTree(settings, locale, writer, indent, currentIndent, lineNumber);
619        } else {
620            for (int i=0; i < getChildCount(); i++) {
621                getChild(i).printTree(settings, locale, writer, indent, currentIndent+indent, lineNumber);
622            }
623        }
624    }
625
626    /** {@inheritDoc} */
627    @Override
628    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
629                                                        justification="Specific log message format")
630    public void getUsageTree(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
631        if (!(getObject() instanceof AbstractMaleSocket)) {
632            log.debug("*@ {} :: {}", level, this.getLongDescription());
633            _object.getUsageDetail(level, bean, report, cdl);
634        }
635
636        if (getObject() instanceof MaleSocket) {
637            getObject().getUsageTree(level, bean, report, cdl);
638        } else {
639            level++;
640            for (int i=0; i < getChildCount(); i++) {
641                getChild(i).getUsageTree(level, bean, report, cdl);
642            }
643        }
644    }
645
646    /** {@inheritDoc} */
647    @Override
648    public void getUsageDetail(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) {
649    }
650
651    @Override
652    public BaseManager<? extends NamedBean> getManager() {
653        return _manager;
654    }
655
656    @Override
657    public final Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames)
658            throws JmriException {
659
660        MaleSocket maleSocket = (MaleSocket)getObject().getDeepCopy(systemNames, userNames);
661
662        maleSocket.setComment(this.getComment());
663        if (maleSocket.getDebugConfig() != null) {
664            maleSocket.setDebugConfig(maleSocket.getDebugConfig().getCopy());
665        }
666        maleSocket.setEnabledFlag(isEnabled());
667        maleSocket.setListen(getListen());
668        maleSocket.setErrorHandlingType(getErrorHandlingType());
669        maleSocket.setLocked(isLocked());
670        maleSocket.setSystem(false);    // If a system item is copied, the new item is not treated as system
671        maleSocket.setCatchAbortExecution(getCatchAbortExecution());
672
673        for (VariableData data : _localVariables) {
674            maleSocket.addLocalVariable(data._name, data._initialValueType, data._initialValueData);
675        }
676
677        return maleSocket;
678    }
679
680    @Override
681    public final Base deepCopyChildren(Base original, Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
682        getObject().deepCopyChildren(original, systemNames, userNames);
683        return this;
684    }
685
686    /**
687     * Disposes this object.
688     * This must remove _all_ connections!
689     */
690    abstract protected void disposeMe();
691
692    /** {@inheritDoc} */
693    @Override
694    public final void dispose() {
695        for (int i=0; i < getChildCount(); i++) {
696            getChild(i).dispose();
697        }
698        disposeMe();
699    }
700
701    @Override
702    public ErrorHandlingType getErrorHandlingType() {
703        if (getObject() instanceof MaleSocket) {
704            return ((MaleSocket)getObject()).getErrorHandlingType();
705        } else {
706            return _errorHandlingType;
707        }
708    }
709
710    @Override
711    public void setErrorHandlingType(ErrorHandlingType errorHandlingType)
712    {
713        if (getObject() instanceof MaleSocket) {
714            ((MaleSocket)getObject()).setErrorHandlingType(errorHandlingType);
715        } else {
716            _errorHandlingType = errorHandlingType;
717        }
718    }
719
720    /**
721     * Executes the error handling module.
722     * @param  item           the item that had the error
723     * @param  message        the error message
724     * @param  messageList    a list of error messages
725     * @param  e              the exception that has happened
726     */
727    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings({"SLF4J_SIGN_ONLY_FORMAT"})
728            // justification="The message is on several lines")
729    public void executeErrorHandlingModule(
730            Base item,
731            String message,
732            List<String> messageList,
733            Exception e) {
734
735        // Don't call the error handling module if it doesnt exist.
736        Module errorHandlingModule = InstanceManager.getDefault(ModuleManager.class)
737                .getBySystemName(LogixNG_Manager.ERROR_HANDLING_MODULE_NAME);
738        if (errorHandlingModule == null) {
739            return;
740        }
741
742        // Don't call the error handling module if it hasn't any children.
743        if (!ErrorHandlingModuleClass.INSTANCE.errorHandlingModule.getRootSocket().isConnected()) {
744            return;
745        }
746
747        // Don't call the error handling module recursively. It would happen
748        // if there is an error in the error handling module.
749        if (item.getModule() == ErrorHandlingModuleClass.INSTANCE.errorHandlingModule) {
750            log.warn("Exception in LogixNG error handling module. Can't execute error handling module recursively.");
751            if (messageList != null) {
752                for (String s : messageList) {
753                    log.warn("   {}", s);
754                }
755            } else {
756                log.warn("   {}", message);
757            }
758            log.warn("Exception: ", e);
759        }
760
761        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("logixng", item.getLogixNG());
762        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("conditionalng", item.getConditionalNG());
763        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("module", item.getModule());
764        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("item", item);
765        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("message", message);
766        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("messageList", messageList);
767        ErrorHandlingModuleClass.INSTANCE._variablesWithValues.put("exception", e);
768        ErrorHandlingModuleClass.INSTANCE.errorHandlingConditionalNG.execute();
769    }
770
771    @Override
772    public void handleError(Base item, String message, JmriException e, Logger log) throws JmriException {
773
774        // Always throw AbortConditionalNGExecutionException exceptions
775        if (!_catchAbortExecution && (e instanceof AbortConditionalNGExecutionException)) throw e;
776
777        ErrorHandlingType errorHandlingType = getErrorHandlingType();
778        if (errorHandlingType == ErrorHandlingType.Default) {
779            errorHandlingType = InstanceManager.getDefault(LogixNGPreferences.class)
780                    .getErrorHandlingType();
781        }
782
783        executeErrorHandlingModule(item, message, null, e);
784
785        switch (errorHandlingType) {
786            case ShowDialogBox:
787                boolean abort = ThreadingUtil.runOnGUIwithReturn(() -> {
788                    ErrorHandlingDialog dialog = new ErrorHandlingDialog();
789                    return dialog.showDialog(item, message);
790                });
791                if (abort) throw new AbortConditionalNGExecutionException(this, e);
792                break;
793
794            case LogError:
795                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
796                break;
797
798            case LogErrorOnce:
799                LoggingUtil.warnOnce(log, "item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
800                break;
801
802            case ThrowException:
803                throw e;
804
805            case AbortExecution:
806                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
807                throw new AbortConditionalNGExecutionException(this, e);
808
809            case AbortWithoutError:
810                throw new AbortConditionalNG_IgnoreException(this, e);
811
812            default:
813                throw e;
814        }
815    }
816
817    @Override
818    public void handleError(
819            Base item,
820            String message,
821            List<String> messageList,
822            JmriException e,
823            Logger log)
824            throws JmriException {
825
826        ErrorHandlingType errorHandlingType = getErrorHandlingType();
827        if (errorHandlingType == ErrorHandlingType.Default) {
828            errorHandlingType = InstanceManager.getDefault(LogixNGPreferences.class)
829                    .getErrorHandlingType();
830        }
831
832        executeErrorHandlingModule(item, message, messageList, e);
833
834        switch (errorHandlingType) {
835            case ShowDialogBox:
836                boolean abort = ThreadingUtil.runOnGUIwithReturn(() -> {
837                    ErrorHandlingDialog_MultiLine dialog = new ErrorHandlingDialog_MultiLine();
838                    return dialog.showDialog(item, message, messageList);
839                });
840                if (abort) throw new AbortConditionalNGExecutionException(this, e);
841                break;
842
843            case LogError:
844                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
845                break;
846
847            case LogErrorOnce:
848                LoggingUtil.warnOnce(log, "item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
849                break;
850
851            case ThrowException:
852                throw e;
853
854            case AbortExecution:
855                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
856                throw new AbortConditionalNGExecutionException(this, e);
857
858            case AbortWithoutError:
859                throw new AbortConditionalNG_IgnoreException(this, e);
860
861            default:
862                throw e;
863        }
864    }
865
866    @Override
867    public void handleError(Base item, String message, RuntimeException e, Logger log) throws JmriException {
868
869        ErrorHandlingType errorHandlingType = getErrorHandlingType();
870        if (errorHandlingType == ErrorHandlingType.Default) {
871            errorHandlingType = InstanceManager.getDefault(LogixNGPreferences.class)
872                    .getErrorHandlingType();
873        }
874
875        executeErrorHandlingModule(item, message, null, e);
876
877        switch (errorHandlingType) {
878            case ShowDialogBox:
879                boolean abort = ThreadingUtil.runOnGUIwithReturn(() -> {
880                    ErrorHandlingDialog dialog = new ErrorHandlingDialog();
881                    return dialog.showDialog(item, message);
882                });
883                if (abort) throw new AbortConditionalNGExecutionException(this, e);
884                break;
885
886            case LogError:
887//                e.printStackTrace();
888                log.error("item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
889                break;
890
891            case LogErrorOnce:
892//                e.printStackTrace();
893                LoggingUtil.warnOnce(log, "item {}, {} thrown an exception: {}", item.toString(), getObject().toString(), e, e);
894                break;
895
896            case ThrowException:
897                throw e;
898
899            case AbortExecution:
900                throw new AbortConditionalNGExecutionException(this, e);
901
902            case AbortWithoutError:
903                throw new AbortConditionalNG_IgnoreException(this, e);
904
905            default:
906                throw e;
907        }
908    }
909
910    /** {@inheritDoc} */
911    @Override
912    public void getListenerRefsIncludingChildren(List<String> list) {
913        list.addAll(getListenerRefs());
914        for (int i=0; i < getChildCount(); i++) {
915            getChild(i).getListenerRefsIncludingChildren(list);
916        }
917    }
918
919    @Override
920    public boolean hasChild(@Nonnull Base b) {
921        return getObject() == b;
922    }
923
924    /** {@inheritDoc} */
925    @Override
926    public String toString() {
927        return getObject().toString();
928    }
929
930    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractMaleSocket.class);
931}