001package jmri.jmrit.logixng.implementation;
002
003import java.io.PrintWriter;
004import java.util.*;
005
006import jmri.InstanceManager;
007import jmri.JmriException;
008import jmri.Manager;
009import jmri.NamedBean;
010import jmri.NamedBeanUsageReport;
011import jmri.implementation.AbstractNamedBean;
012import jmri.jmrit.logixng.*;
013
014import org.apache.commons.lang3.mutable.MutableInt;
015
016/**
017 * The default implementation of LogixNG.
018 *
019 * @author Daniel Bergqvist Copyright 2018
020 * @author Dave Sand        Copyright 2021
021 */
022public class DefaultLogixNG extends AbstractNamedBean
023        implements LogixNG {
024
025    private final LogixNG_Manager _manager = InstanceManager.getDefault(LogixNG_Manager.class);
026    private boolean _startup = true;
027    private boolean _inline = false;
028    private InlineLogixNG _inlineLogixNG = null;
029    private boolean _enabled = false;
030    private boolean _isActive = false;
031    private final List<ConditionalNG_Entry> _conditionalNG_Entries = new ArrayList<>();
032
033
034    public DefaultLogixNG(String sys, String user) throws BadUserNameException, BadSystemNameException  {
035        super(sys, user);
036
037        // Do this test here to ensure all the tests are using correct system names
038        Manager.NameValidity isNameValid = InstanceManager.getDefault(LogixNG_Manager.class).validSystemNameFormat(mSystemName);
039        if (isNameValid != Manager.NameValidity.VALID) {
040            throw new IllegalArgumentException("system name is not valid");
041        }
042    }
043
044    public DefaultLogixNG(String sys, String user, boolean inline) throws BadUserNameException, BadSystemNameException  {
045        super(sys, user);
046
047        _inline = inline;
048
049        // Do this test here to ensure all the tests are using correct system names
050        Manager.NameValidity isNameValid = InstanceManager.getDefault(LogixNG_Manager.class).validSystemNameFormat(mSystemName);
051        if (isNameValid != Manager.NameValidity.VALID) {
052            throw new IllegalArgumentException("system name is not valid");
053        }
054    }
055
056    /** {@inheritDoc} */
057    @Override
058    public Base getParent() {
059        return null;
060    }
061
062    /** {@inheritDoc} */
063    @Override
064    public void setParent(Base parent) {
065        throw new UnsupportedOperationException("A LogixNG cannot have a parent");
066    }
067
068    @Override
069    public String getBeanType() {
070        return Bundle.getMessage("BeanNameLogixNG");
071    }
072
073    @Override
074    public void setState(int s) throws JmriException {
075        log.warn("Unexpected call to setState in DefaultLogixNG.");  // NOI18N
076    }
077
078    @Override
079    public int getState() {
080        log.warn("Unexpected call to getState in DefaultLogixNG.");  // NOI18N
081        return UNKNOWN;
082    }
083
084    @Override
085    public String getShortDescription(Locale locale) {
086        return "LogixNG";
087    }
088
089    @Override
090    public String getLongDescription(Locale locale) {
091        return "LogixNG: "+getDisplayName();
092    }
093
094    @Override
095    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
096        throw new UnsupportedOperationException("Not supported.");
097    }
098
099    @Override
100    public int getChildCount() {
101        throw new UnsupportedOperationException("Not supported.");
102    }
103
104    @Override
105    public Category getCategory() {
106        throw new UnsupportedOperationException("Not supported.");
107    }
108
109    /** {@inheritDoc} */
110    @Override
111    final public void setup() {
112        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
113            if ( entry._conditionalNG == null
114                    || !entry._conditionalNG.getSystemName()
115                            .equals(entry._systemName)) {
116
117                String systemName = entry._systemName;
118                if (systemName != null) {
119                    entry._conditionalNG =
120                            InstanceManager.getDefault(ConditionalNG_Manager.class)
121                                    .getBySystemName(systemName);
122                    if (entry._conditionalNG != null) {
123                        entry._conditionalNG.setup();
124                    } else {
125                        log.error("cannot load conditionalNG {}", systemName);
126                    }
127                }
128            } else {
129                entry._conditionalNG.setup();
130            }
131        }
132    }
133
134    /** {@inheritDoc} */
135    @Override
136    public void clearStartup() {
137        _startup = false;
138    }
139
140    /** {@inheritDoc} */
141    @Override
142    public boolean isStartup() {
143        return _startup;
144    }
145
146    /** {@inheritDoc} */
147    @Override
148    public void setInline(boolean inline) {
149        boolean old = _inline;
150        _inline = inline;
151        firePropertyChange(LogixNG.PROPERTY_INLINE, old, _inline);
152    }
153
154    /** {@inheritDoc} */
155    @Override
156    public boolean isInline() {
157        return _inline;
158    }
159
160    /** {@inheritDoc} */
161    @Override
162    public void setInlineLogixNG(InlineLogixNG inlineLogixNG) {
163        _inlineLogixNG = inlineLogixNG;
164    }
165
166    /** {@inheritDoc} */
167    @Override
168    public InlineLogixNG getInlineLogixNG() {
169        return _inlineLogixNG;
170    }
171
172    /** {@inheritDoc} */
173    @Override
174    public void setEnabled(boolean enable) {
175        boolean old = _enabled;
176        _enabled = enable;
177        checkIfActiveAndEnabled();
178        firePropertyChange(PROPERTY_ENABLED, old, _enabled);
179    }
180
181    /** {@inheritDoc} */
182    @Override
183    public void activate() {
184        _isActive = true;
185    }
186
187    /** {@inheritDoc} */
188    @Override
189    public void setActive(boolean active) {
190        _isActive = active;
191        checkIfActiveAndEnabled();
192    }
193
194    private void checkIfActiveAndEnabled() {
195        if (isActive()) {
196            registerListeners();
197            execute(true, true);
198        } else {
199            unregisterListeners();
200        }
201    }
202
203    /** {@inheritDoc} */
204    @Override
205    public boolean isEnabled() {
206        return _enabled;
207    }
208
209    /** {@inheritDoc} */
210    @Override
211    public String getConditionalNG_SystemName(int index) {
212        return _conditionalNG_Entries.get(index)._systemName;
213    }
214
215    /** {@inheritDoc} */
216    @Override
217    public void setConditionalNG_SystemName(int index, String systemName) {
218        if (index == _conditionalNG_Entries.size()) {
219            _conditionalNG_Entries.add(new ConditionalNG_Entry(systemName));
220        } else {
221            ConditionalNG_Entry entry = _conditionalNG_Entries.get(index);
222            entry._systemName = systemName;
223            entry._conditionalNG = null;
224        }
225    }
226
227    /** {@inheritDoc} */
228    @Override
229    public int getNumConditionalNGs() {
230        return _conditionalNG_Entries.size();
231    }
232
233    /** {@inheritDoc} */
234    @Override
235    public void swapConditionalNG(int nextInOrder, int row) {
236        if (row <= nextInOrder) {
237            return;
238        }
239        ConditionalNG_Entry temp = _conditionalNG_Entries.get(row);
240        for (int i = row; i > nextInOrder; i--) {
241            _conditionalNG_Entries.set(i, _conditionalNG_Entries.get(i - 1));
242        }
243        _conditionalNG_Entries.set(nextInOrder, temp);
244    }
245
246    /** {@inheritDoc} */
247    @Override
248    public ConditionalNG getConditionalNG(int order) {
249        try {
250            return _conditionalNG_Entries.get(order)._conditionalNG;
251        } catch (java.lang.IndexOutOfBoundsException ioob) {
252            return null;
253        }
254    }
255
256    /** {@inheritDoc} */
257    @Override
258    public boolean addConditionalNG(ConditionalNG conditionalNG) {
259        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
260            if (conditionalNG.getSystemName().equals(entry._systemName)) {
261                if (entry._conditionalNG == null) {
262                    // Normally this will be during xml loading
263                    entry._conditionalNG = conditionalNG;
264                    return true;
265                } else {
266                    log.error("ConditionalNG '{}' has already been added to LogixNG '{}'", conditionalNG.getSystemName(), getSystemName());  // NOI18N
267                    return false;
268                }
269            }
270
271        }
272
273        ConditionalNG_Entry entry = new ConditionalNG_Entry(conditionalNG, conditionalNG.getSystemName());
274        _conditionalNG_Entries.add(entry);
275        conditionalNG.setParent(this);
276        return true;
277    }
278
279    /** {@inheritDoc} */
280    @Override
281    public ConditionalNG getConditionalNG(String systemName) {
282        for (int i = 0; i < getNumConditionalNGs(); i++) {
283            if (systemName.equals(_conditionalNG_Entries.get(i)._systemName)) {
284                return _conditionalNG_Entries.get(i)._conditionalNG;
285            }
286        }
287        return null;
288    }
289
290    /** {@inheritDoc} */
291    @Override
292    public ConditionalNG getConditionalNGByUserName(String userName) {
293        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
294            if (userName.equals(entry._conditionalNG.getUserName())) {
295                return entry._conditionalNG;
296            }
297        }
298        return null;
299    }
300
301    /** {@inheritDoc} */
302    @Override
303    public void deleteConditionalNG(ConditionalNG conditionalNG) {
304        if (_conditionalNG_Entries.size() <= 0) {
305            log.error("attempt to delete ConditionalNG not in LogixNG: {}", conditionalNG.getSystemName());  // NOI18N
306            return;
307        }
308
309        boolean found = false;
310        // Remove Conditional from this logix
311        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
312            if (conditionalNG == entry._conditionalNG) {
313                _conditionalNG_Entries.remove(entry);
314                found = true;
315                break;
316            }
317        }
318        if (!found) {
319            log.error("attempt to delete ConditionalNG not in LogixNG: {}", conditionalNG.getSystemName());  // NOI18N
320        }
321    }
322
323    /** {@inheritDoc} */
324    @Override
325    public boolean isActive() {
326        return _enabled && _isActive && _manager.isActive();
327    }
328
329    /** {@inheritDoc} */
330    @Override
331    public boolean isActivated() {
332        return _isActive;
333    }
334
335    /** {@inheritDoc} */
336    @Override
337    public void execute() {
338        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
339            entry._conditionalNG.execute();
340        }
341    }
342
343    /** {@inheritDoc} */
344    @Override
345    public void execute(boolean allowRunDelayed) {
346        execute(allowRunDelayed, false);
347    }
348
349    /** {@inheritDoc} */
350    @Override
351    public void execute(boolean allowRunDelayed, boolean isStartup) {
352        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
353            ConditionalNG cng = entry._conditionalNG;
354            if (cng.isEnabled() && (!isStartup || cng.isExecuteAtStartup())) {
355                cng.execute(allowRunDelayed);
356            }
357        }
358    }
359
360    /** {@inheritDoc} */
361    @Override
362    public ConditionalNG getConditionalNG() {
363        throw new UnsupportedOperationException("Not supported.");
364    }
365
366    /** {@inheritDoc} */
367    @Override
368    public LogixNG getLogixNG() {
369        return this;
370    }
371
372    /** {@inheritDoc} */
373    @Override
374    public final Base getRoot() {
375        return this;
376    }
377
378    /** {@inheritDoc} */
379    @Override
380    public boolean setParentForAllChildren(List<String> errors) {
381        boolean result = true;
382        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
383            if (entry._conditionalNG != null) {
384                entry._conditionalNG.setParent(this);
385                result = result && entry._conditionalNG.setParentForAllChildren(errors);
386            }
387        }
388        return result;
389    }
390
391    /** {@inheritDoc} */
392    @Override
393    public void registerListeners() {
394        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
395            entry._conditionalNG.registerListeners();
396        }
397    }
398
399    /** {@inheritDoc} */
400    @Override
401    public void unregisterListeners() {
402        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
403            entry._conditionalNG.unregisterListeners();
404        }
405    }
406
407    protected void printTreeRow(
408            PrintTreeSettings settings,
409            Locale locale,
410            PrintWriter writer,
411            String currentIndent,
412            MutableInt lineNumber) {
413
414        if (settings._printLineNumbers) {
415            writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1)));
416        }
417        writer.append(currentIndent);
418        writer.append(getLongDescription(locale));
419        writer.println();
420    }
421
422    /** {@inheritDoc} */
423    @Override
424    public void printTree(
425            PrintTreeSettings settings,
426            PrintWriter writer,
427            String indent,
428            MutableInt lineNumber) {
429
430        printTree(settings, Locale.getDefault(), writer, indent, "", lineNumber);
431    }
432
433    /** {@inheritDoc} */
434    @Override
435    public void printTree(
436            PrintTreeSettings settings,
437            Locale locale,
438            PrintWriter writer,
439            String indent,
440            MutableInt lineNumber) {
441
442        printTree(settings, locale, writer, indent, "", lineNumber);
443    }
444
445    /** {@inheritDoc} */
446    @Override
447    public void printTree(
448            PrintTreeSettings settings,
449            Locale locale,
450            PrintWriter writer,
451            String indent,
452            String currentIndent,
453            MutableInt lineNumber) {
454
455        printTreeRow(settings, locale, writer, currentIndent, lineNumber);
456
457        for (int i=0; i < this.getNumConditionalNGs(); i++) {
458            getConditionalNG(i).printTree(settings, locale, writer, indent, currentIndent+indent, lineNumber);
459//            writer.println();
460        }
461    }
462
463    @Override
464    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) {
465        throw new UnsupportedOperationException("Not supported yet.");
466    }
467
468    @Override
469    public Base deepCopyChildren(Base original, Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
470        throw new UnsupportedOperationException("Not supported");
471    }
472
473    private static class ConditionalNG_Entry {
474        private String _systemName;
475        private ConditionalNG _conditionalNG;
476
477        private ConditionalNG_Entry(ConditionalNG conditionalNG, String systemName) {
478            _systemName = systemName;
479            _conditionalNG = conditionalNG;
480        }
481
482        private ConditionalNG_Entry(ConditionalNG conditionalNG) {
483            this._conditionalNG = conditionalNG;
484        }
485
486        private ConditionalNG_Entry(String systemName) {
487            this._systemName = systemName;
488        }
489
490        @Override
491        public String toString() {
492            StringBuilder sb = new StringBuilder("ConditionalNG_Entry: name =");
493            sb.append(_systemName);
494            sb.append(", cdl = ");
495            sb.append(_conditionalNG == null ? "----" : _conditionalNG.getDisplayName());
496            return sb.toString();
497        }
498    }
499
500    /** {@inheritDoc} */
501    @Override
502    public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) {
503        List<NamedBeanUsageReport> report = new ArrayList<>();
504        if (bean != null) {
505            getUsageTree(0, bean, report, null);
506        }
507        return report;
508    }
509
510    /** {@inheritDoc} */
511    @Override
512    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
513                                                        justification="Specific log message format")
514    public void getUsageTree(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) {
515        log.debug("** {} :: {}", level, this.getClass().getName());
516
517        level++;
518        for (int i=0; i < this.getNumConditionalNGs(); i++) {
519            getConditionalNG(i).getUsageTree(level, bean, report, getConditionalNG(i));
520        }
521    }
522
523    /** {@inheritDoc} */
524    @Override
525    public void getUsageDetail(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) {
526    }
527
528    /** {@inheritDoc} */
529    @Override
530    public void getListenerRefsIncludingChildren(List<String> list) {
531        list.addAll(getListenerRefs());
532        for (int i=0; i < getNumConditionalNGs(); i++) {
533            getConditionalNG(i).getListenerRefsIncludingChildren(list);
534        }
535    }
536
537    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultLogixNG.class);
538}