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