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);
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 void execute() {
332        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
333            entry._conditionalNG.execute();
334        }
335    }
336
337    /** {@inheritDoc} */
338    @Override
339    public void execute(boolean allowRunDelayed) {
340        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
341            entry._conditionalNG.execute(allowRunDelayed);
342        }
343    }
344
345    /** {@inheritDoc} */
346    @Override
347    public ConditionalNG getConditionalNG() {
348        throw new UnsupportedOperationException("Not supported.");
349    }
350
351    /** {@inheritDoc} */
352    @Override
353    public LogixNG getLogixNG() {
354        return this;
355    }
356
357    /** {@inheritDoc} */
358    @Override
359    public final Base getRoot() {
360        return this;
361    }
362
363    /** {@inheritDoc} */
364    @Override
365    public boolean setParentForAllChildren(List<String> errors) {
366        boolean result = true;
367        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
368            if (entry._conditionalNG != null) {
369                entry._conditionalNG.setParent(this);
370                result = result && entry._conditionalNG.setParentForAllChildren(errors);
371            }
372        }
373        return result;
374    }
375
376    /** {@inheritDoc} */
377    @Override
378    public void registerListeners() {
379        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
380            entry._conditionalNG.registerListeners();
381        }
382    }
383
384    /** {@inheritDoc} */
385    @Override
386    public void unregisterListeners() {
387        for (ConditionalNG_Entry entry : _conditionalNG_Entries) {
388            entry._conditionalNG.unregisterListeners();
389        }
390    }
391
392    protected void printTreeRow(
393            PrintTreeSettings settings,
394            Locale locale,
395            PrintWriter writer,
396            String currentIndent,
397            MutableInt lineNumber) {
398
399        if (settings._printLineNumbers) {
400            writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1)));
401        }
402        writer.append(currentIndent);
403        writer.append(getLongDescription(locale));
404        writer.println();
405    }
406
407    /** {@inheritDoc} */
408    @Override
409    public void printTree(
410            PrintTreeSettings settings,
411            PrintWriter writer,
412            String indent,
413            MutableInt lineNumber) {
414
415        printTree(settings, Locale.getDefault(), writer, indent, "", lineNumber);
416    }
417
418    /** {@inheritDoc} */
419    @Override
420    public void printTree(
421            PrintTreeSettings settings,
422            Locale locale,
423            PrintWriter writer,
424            String indent,
425            MutableInt lineNumber) {
426
427        printTree(settings, locale, writer, indent, "", lineNumber);
428    }
429
430    /** {@inheritDoc} */
431    @Override
432    public void printTree(
433            PrintTreeSettings settings,
434            Locale locale,
435            PrintWriter writer,
436            String indent,
437            String currentIndent,
438            MutableInt lineNumber) {
439
440        printTreeRow(settings, locale, writer, currentIndent, lineNumber);
441
442        for (int i=0; i < this.getNumConditionalNGs(); i++) {
443            getConditionalNG(i).printTree(settings, locale, writer, indent, currentIndent+indent, lineNumber);
444//            writer.println();
445        }
446    }
447
448    @Override
449    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) {
450        throw new UnsupportedOperationException("Not supported yet.");
451    }
452
453    @Override
454    public Base deepCopyChildren(Base original, Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
455        throw new UnsupportedOperationException("Not supported");
456    }
457
458    private static class ConditionalNG_Entry {
459        private String _systemName;
460        private ConditionalNG _conditionalNG;
461
462        private ConditionalNG_Entry(ConditionalNG conditionalNG, String systemName) {
463            _systemName = systemName;
464            _conditionalNG = conditionalNG;
465        }
466
467        private ConditionalNG_Entry(ConditionalNG conditionalNG) {
468            this._conditionalNG = conditionalNG;
469        }
470
471        private ConditionalNG_Entry(String systemName) {
472            this._systemName = systemName;
473        }
474
475        @Override
476        public String toString() {
477            StringBuilder sb = new StringBuilder("ConditionalNG_Entry: name =");
478            sb.append(_systemName);
479            sb.append(", cdl = ");
480            sb.append(_conditionalNG == null ? "----" : _conditionalNG.getDisplayName());
481            return sb.toString();
482        }
483    }
484
485    /** {@inheritDoc} */
486    @Override
487    public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) {
488        List<NamedBeanUsageReport> report = new ArrayList<>();
489        if (bean != null) {
490            getUsageTree(0, bean, report, null);
491        }
492        return report;
493    }
494
495    /** {@inheritDoc} */
496    @Override
497    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
498                                                        justification="Specific log message format")
499    public void getUsageTree(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) {
500        log.debug("** {} :: {}", level, this.getClass().getName());
501
502        level++;
503        for (int i=0; i < this.getNumConditionalNGs(); i++) {
504            getConditionalNG(i).getUsageTree(level, bean, report, getConditionalNG(i));
505        }
506    }
507
508    /** {@inheritDoc} */
509    @Override
510    public void getUsageDetail(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) {
511    }
512
513    /** {@inheritDoc} */
514    @Override
515    public void getListenerRefsIncludingChildren(List<String> list) {
516        list.addAll(getListenerRefs());
517        for (int i=0; i < getNumConditionalNGs(); i++) {
518            getConditionalNG(i).getListenerRefsIncludingChildren(list);
519        }
520    }
521
522    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultLogixNG.class);
523}