001package jmri.jmrit.logixng.util;
002
003import java.beans.VetoableChangeListener;
004import java.util.*;
005
006import javax.annotation.Nonnull;
007
008import jmri.*;
009import jmri.jmrit.logixng.*;
010import jmri.jmrit.logixng.util.parser.*;
011import jmri.jmrit.logixng.util.parser.RecursiveDescentParser;
012import jmri.util.TypeConversionUtil;
013
014/**
015 * Select a string list for LogixNG actions and expressions.
016 *
017 * @author Daniel Bergqvist (C) 2025
018 */
019public class LogixNG_SelectStringList implements VetoableChangeListener {
020
021    private boolean _onlyDirectAddressingAllowed;
022
023    private NamedBeanAddressing _addressing = NamedBeanAddressing.Direct;
024    private final List<String> _list = new ArrayList<>();
025    private String _localVariable = "";
026    private String _formula = "";
027    private ExpressionNode _expressionNode;
028
029
030    public void setOnlyDirectAddressingAllowed() {
031        _onlyDirectAddressingAllowed = true;
032    }
033
034    public boolean isOnlyDirectAddressingAllowed() {
035        return _onlyDirectAddressingAllowed;
036    }
037
038    public void copy(LogixNG_SelectStringList copy) throws ParserException {
039        copy.setAddressing(_addressing);
040        copy._list.addAll(_list);
041        copy.setLocalVariable(_localVariable);
042        copy.setFormula(_formula);
043    }
044
045    public void setAddressing(@Nonnull NamedBeanAddressing addressing) throws ParserException {
046        if (_onlyDirectAddressingAllowed && (addressing != NamedBeanAddressing.Direct)) {
047            throw new IllegalArgumentException("Addressing must be Direct");
048        }
049        this._addressing = addressing;
050        parseFormula();
051    }
052
053    public boolean isDirectAddressing() {
054        return _addressing == NamedBeanAddressing.Direct;
055    }
056
057    public NamedBeanAddressing getAddressing() {
058        return _addressing;
059    }
060
061    public List<String> getList() {
062        return _list;
063    }
064
065    public void setLocalVariable(@Nonnull String localVariable) {
066        _localVariable = localVariable;
067    }
068
069    public String getLocalVariable() {
070        return _localVariable;
071    }
072
073    public void setFormula(@Nonnull String formula) throws ParserException {
074        _formula = formula;
075        parseFormula();
076    }
077
078    public String getFormula() {
079        return _formula;
080    }
081
082    private void parseFormula() throws ParserException {
083        if (_addressing == NamedBeanAddressing.Formula) {
084            Map<String, Variable> variables = new HashMap<>();
085
086            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
087            _expressionNode = parser.parseExpression(_formula);
088        } else {
089            _expressionNode = null;
090        }
091    }
092
093    public List<String> evaluateValue(ConditionalNG conditionalNG) throws JmriException {
094
095        if (_addressing == NamedBeanAddressing.Direct) {
096            return _list;
097        } else {
098            Object val;
099
100            switch (_addressing) {
101                case LocalVariable:
102                    SymbolTable symbolNamedBean = conditionalNG.getSymbolTable();
103                    val = symbolNamedBean.getValue(_localVariable);
104                    break;
105
106                case Formula:
107                    val = _expressionNode != null
108                            ? _expressionNode.calculate(conditionalNG.getSymbolTable())
109                            : null;
110                    break;
111
112                default:
113                    throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name());
114            }
115
116            if (val instanceof String[]) {
117                return Arrays.asList( (String[])val );
118            } else if (val instanceof List) {
119                List<String> resultList = new ArrayList<>();
120                for (Object o : (List<?>)val) {
121                    resultList.add(TypeConversionUtil.convertToString(o, false));
122                }
123                return resultList;
124            } else {
125                throw new JmriException("value is not an array of String or a List of String. Class name: "
126                        + (val != null ? val.getClass().getName() : null));
127            }
128        }
129    }
130
131    public String getDescription(Locale locale) {
132        String enumName;
133
134        switch (_addressing) {
135            case Direct:
136                enumName = Bundle.getMessage(locale, "AddressByDirect", String.join(" ::: ", _list));
137                break;
138
139            case LocalVariable:
140                enumName = Bundle.getMessage(locale, "AddressByLocalVariable", _localVariable);
141                break;
142
143            case Formula:
144                enumName = Bundle.getMessage(locale, "AddressByFormula", _formula);
145                break;
146
147            default:
148                throw new IllegalArgumentException("invalid _addressing: " + _addressing.name());
149        }
150        return enumName;
151    }
152
153    /**
154     * Register listeners if this object needs that.
155     */
156    public void registerListeners() {
157        // Do nothing
158    }
159
160    /**
161     * Unregister listeners if this object needs that.
162     */
163    public void unregisterListeners() {
164        // Do nothing
165    }
166
167    @Override
168    public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException {
169        // Do nothing
170    }
171
172//    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNG_SelectStringArray.class);
173}