001package jmri.jmrit.logixng.implementation;
002
003import java.util.*;
004
005import jmri.*;
006import jmri.jmrit.logixng.ConditionalNG;
007import jmri.jmrit.logixng.GlobalVariable;
008import jmri.jmrit.logixng.GlobalVariableManager;
009import jmri.jmrit.logixng.Module.Parameter;
010import jmri.jmrit.logixng.Stack;
011import jmri.jmrit.logixng.Stack.ValueAndType;
012import jmri.jmrit.logixng.SymbolTable;
013
014/**
015 * The default implementation of a NamedTable
016 *
017 * @author Daniel Bergqvist 2020
018 */
019public class DefaultSymbolTable implements SymbolTable {
020
021    private final SymbolTable _prevSymbolTable;
022    private final Stack _stack;
023    private final int _firstSymbolIndex;
024    private final Map<String, Symbol> _symbols = new HashMap<>();
025
026
027    /**
028     * Create a new instance of DefaultSymbolTable with no previous symbol table.
029     */
030    public DefaultSymbolTable() {
031        _prevSymbolTable = null;
032        _stack = new DefaultStack();
033        _firstSymbolIndex = _stack.getCount();
034    }
035
036    /**
037     * Create a new instance of DefaultSymbolTable with previous symbol table
038     * and the stack from a ConditionalNG.
039     * @param currentConditionalNG the ConditionalNG
040     */
041    public DefaultSymbolTable(ConditionalNG currentConditionalNG) {
042        _prevSymbolTable = currentConditionalNG.getSymbolTable();
043        _stack = currentConditionalNG.getStack();
044        _firstSymbolIndex = _stack.getCount();
045    }
046
047    /**
048     * Create a new instance of DefaultSymbolTable from a previous symbol table
049     * and a stack.
050     * @param prevSymbolTable the previous symbol table
051     */
052    public DefaultSymbolTable(SymbolTable prevSymbolTable) {
053        _prevSymbolTable = null;
054        _symbols.putAll(prevSymbolTable.getSymbols());
055        _stack = new DefaultStack();
056
057        // Push the values in the order of the symbols indexes, so that the
058        // stack count is correct and new symbols are created after the
059        // copied symbols.
060        int count = 0;
061        for (Symbol symbol : _symbols.values()) {
062            count = Math.max(count, symbol.getIndex() + 1);
063        }
064        ValueAndType[] values = new ValueAndType[count];
065        for (Symbol symbol : _symbols.values()) {
066            values[symbol.getIndex()] = prevSymbolTable.getValueAndType(symbol.getName());
067        }
068        for (ValueAndType valueAndType : values) {
069            if (valueAndType != null) {
070                _stack.push(new ValueAndType(valueAndType._type, valueAndType._value));
071            } else {
072                _stack.push(new ValueAndType(InitialValueType.None, null));
073            }
074        }
075        _firstSymbolIndex = 0;
076    }
077
078    /**
079     * Get the previous symbol table
080     * @return the symbol table
081     */
082    public SymbolTable getPrevSymbolTable() {
083        return _prevSymbolTable;
084    }
085
086    /** {@inheritDoc} */
087    @Override
088    public Map<String, Symbol> getSymbols() {
089        return Collections.unmodifiableMap(_symbols);
090    }
091
092    /** {@inheritDoc} */
093    @Override
094    public Map<String, Object> getSymbolValues() {
095        Map<String, Object> symbolValues = new HashMap<>();
096        for (Symbol symbol : _symbols.values()) {
097            Object value = _stack.getValueAtIndex(_firstSymbolIndex + symbol.getIndex());
098            symbolValues.put(symbol.getName(), value);
099        }
100        return Collections.unmodifiableMap(symbolValues);
101    }
102
103    /** {@inheritDoc} */
104    @Override
105    public Object getValue(String name) {
106        Symbol symbol = _symbols.get(name);
107        if (symbol != null) {
108            return _stack.getValueAtIndex(_firstSymbolIndex + symbol.getIndex());
109        }
110        GlobalVariable globalVariable = InstanceManager.getDefault(GlobalVariableManager.class).getByUserName(name);
111        if (globalVariable != null) {
112            return globalVariable.getValue();
113        }
114        throw new SymbolNotFound(String.format("Symbol '%s' does not exist in symbol table", name));
115    }
116
117    /** {@inheritDoc} */
118    @Override
119    public ValueAndType getValueAndType(String name) {
120        Symbol symbol = _symbols.get(name);
121        if (symbol != null) {
122            return _stack.getValueAndTypeAtIndex(_firstSymbolIndex + symbol.getIndex());
123        }
124        throw new SymbolNotFound(String.format("Symbol '%s' does not exist in symbol table", name));
125    }
126
127    /** {@inheritDoc} */
128    @Override
129    public boolean hasValue(String name) {
130        if (_symbols.containsKey(name)) return true;
131        GlobalVariable globalVariable = InstanceManager.getDefault(GlobalVariableManager.class).getByUserName(name);
132        return globalVariable != null;
133    }
134
135    /** {@inheritDoc} */
136    @Override
137    public void setValue(String name, Object value) {
138        if (_symbols.get(name) != null) {
139            _stack.setValueAtIndex(_firstSymbolIndex + _symbols.get(name).getIndex(), value);
140        } else {
141            GlobalVariable globalVariable = InstanceManager.getDefault(GlobalVariableManager.class).getByUserName(name);
142            if (globalVariable != null) {
143                globalVariable.setValue(value);
144            } else {
145                throw new IllegalArgumentException(Bundle.getMessage("ExceptionSymbolNotInSymbolTable", name));
146            }
147        }
148    }
149
150    /** {@inheritDoc} */
151    @Override
152    public void printSymbolTable(java.io.PrintWriter stream) {
153        stream.format("printSymbolTable:%n");
154        for (Map.Entry<String, Symbol> entry : _symbols.entrySet()) {
155            stream.format("Key: %s, Name: %s, Index: %d, Value: %s%n",
156                    entry.getKey(),
157                    entry.getValue().getName(),
158                    entry.getValue().getIndex(),
159                    _stack.getValueAtIndex(_firstSymbolIndex + entry.getValue().getIndex()));
160        }
161        stream.format("printSymbolTable done%n");
162    }
163
164    /** {@inheritDoc} */
165    @Override
166    public void createSymbols(Collection<? extends SymbolTable.VariableData> symbolDefinitions) throws JmriException {
167        createSymbols(this, symbolDefinitions);
168    }
169
170    /** {@inheritDoc} */
171    @Override
172    public void createSymbols(SymbolTable symbolTable,
173            Collection<? extends SymbolTable.VariableData> symbolDefinitions)
174            throws JmriException {
175
176        for (SymbolTable.VariableData variable : symbolDefinitions) {
177            Symbol symbol = new DefaultSymbol(variable.getName(), _stack.getCount() - _firstSymbolIndex);
178
179            if (_symbols.containsKey(symbol.getName())) {
180                throw new IllegalArgumentException("Symbol table already contains the variable " + symbol.getName());
181            }
182
183            InitialValueType initialValueType = variable.getInitialValueType();
184            Object initialValue = SymbolTable.getInitialValue(
185                    SymbolTable.Type.Local,
186                    symbol.getName(),
187                    initialValueType,
188                    variable.getInitialValueData(),
189                    symbolTable,
190                    _symbols);
191
192//            System.out.format("Add symbol: %s = %s%n", symbol.getName(), initialValue);
193
194            _stack.push(new ValueAndType(initialValueType, initialValue));
195            _symbols.put(symbol.getName(), symbol);
196        }
197    }
198
199    /** {@inheritDoc} */
200    @Override
201    public void removeSymbols(Collection<? extends SymbolTable.VariableData> symbolDefinitions) throws JmriException {
202        symbolDefinitions.forEach((parameter) -> {
203            _symbols.remove(parameter.getName());
204        });
205    }
206
207    /** {@inheritDoc} */
208    @Override
209    public Stack getStack() {
210        return _stack;
211    }
212
213
214    public static class DefaultSymbol implements Symbol {
215
216        private final String _name;
217        private final int _index;
218
219        public DefaultSymbol(String name, int index) {
220            _name = name;
221            _index = index;
222        }
223
224        /** {@inheritDoc} */
225        @Override
226        public String getName() {
227            return _name;
228        }
229
230        /** {@inheritDoc} */
231        @Override
232        public int getIndex() {
233            return _index;
234        }
235
236    }
237
238
239    public static class DefaultParameter implements Parameter {
240
241        private String _name;
242        private boolean _isInput;
243        private boolean _isOutput;
244
245        public DefaultParameter(String name, boolean isInput, boolean isOutput) {
246            _name = name;
247            _isInput = isInput;
248            _isOutput = isOutput;
249        }
250
251        public DefaultParameter(Parameter parameter) {
252            _name = parameter.getName();
253            _isInput = parameter.isInput();
254            _isOutput = parameter.isOutput();
255        }
256
257        /** {@inheritDoc} */
258        @Override
259        public String getName() {
260            return _name;
261        }
262
263        public void setName(String name) {
264            _name = name;
265        }
266
267        /** {@inheritDoc} */
268        @Override
269        public boolean isInput() {
270            return _isInput;
271        }
272
273        public void setIsInput(boolean value) {
274            _isInput = value;
275        }
276
277        /** {@inheritDoc} */
278        @Override
279        public boolean isOutput() {
280            return _isOutput;
281        }
282
283        public void setIsOutput(boolean value) {
284            _isOutput = value;
285        }
286
287    }
288
289
290//    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultSymbolTable.class);
291
292}