001package jmri.jmrit.logixng.expressions;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.*;
006import java.util.regex.Matcher;
007import java.util.regex.Pattern;
008
009import javax.annotation.Nonnull;
010
011import jmri.*;
012import jmri.jmrit.logixng.*;
013import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean;
014import jmri.jmrit.logixng.util.LogixNG_SelectTable;
015import jmri.util.CompareUtil;
016import jmri.util.CompareUtil.CompareType;
017import jmri.util.CompareUtil.CompareOperation;
018import jmri.util.TypeConversionUtil;
019
020/**
021 * Evaluates the state of a Memory.
022 *
023 * @author Daniel Bergqvist Copyright 2018
024 */
025public class ExpressionMemory extends AbstractDigitalExpression
026        implements PropertyChangeListener {
027
028    private final LogixNG_SelectNamedBean<Memory> _selectNamedBean =
029            new LogixNG_SelectNamedBean<>(
030                    this, Memory.class, InstanceManager.getDefault(MemoryManager.class), this);
031
032    private final LogixNG_SelectNamedBean<Memory> _selectOtherMemoryNamedBean =
033            new LogixNG_SelectNamedBean<>(
034                    this, Memory.class, InstanceManager.getDefault(MemoryManager.class), this);
035
036    private MemoryOperation _memoryOperation = MemoryOperation.Equal;
037    private CompareType _compareType = CompareType.NumberOrString;
038    private CompareTo _compareTo = CompareTo.Value;
039    private boolean _caseInsensitive = false;
040    private String _constantValue = "";
041
042    private String _localVariable = "";
043    private String _regEx = "";
044    private boolean _listenToOtherMemory = true;
045
046    private final LogixNG_SelectTable _selectTable =
047            new LogixNG_SelectTable(this, () -> {return _compareTo == CompareTo.Table;});
048
049
050    public ExpressionMemory(String sys, String user)
051            throws BadUserNameException, BadSystemNameException {
052        super(sys, user);
053    }
054
055    @Override
056    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
057        DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class);
058        String sysName = systemNames.get(getSystemName());
059        String userName = userNames.get(getSystemName());
060        if (sysName == null) sysName = manager.getAutoSystemName();
061        ExpressionMemory copy = new ExpressionMemory(sysName, userName);
062        copy.setComment(getComment());
063        _selectNamedBean.copy(copy._selectNamedBean);
064        _selectOtherMemoryNamedBean.copy(copy._selectOtherMemoryNamedBean);
065        _selectTable.copy(copy._selectTable);
066        copy.setMemoryOperation(_memoryOperation);
067        copy.setCompareType(_compareType);
068        copy.setCompareTo(_compareTo);
069        copy.setCaseInsensitive(_caseInsensitive);
070        copy.setConstantValue(_constantValue);
071        copy.setLocalVariable(_localVariable);
072        copy.setRegEx(_regEx);
073        copy.setListenToOtherMemory(_listenToOtherMemory);
074        return manager.registerExpression(copy).deepCopyChildren(this, systemNames, userNames);
075    }
076
077    public LogixNG_SelectNamedBean<Memory> getSelectNamedBean() {
078        return _selectNamedBean;
079    }
080
081    public LogixNG_SelectNamedBean<Memory> getSelectOtherMemoryNamedBean() {
082        return _selectOtherMemoryNamedBean;
083    }
084
085    public LogixNG_SelectTable getSelectTable() {
086        return _selectTable;
087    }
088
089    public void setLocalVariable(@Nonnull String localVariable) {
090        assertListenersAreNotRegistered(log, "setLocalVariable");
091        _localVariable = localVariable;
092    }
093
094    public String getLocalVariable() {
095        return _localVariable;
096    }
097
098    public void setConstantValue(String constantValue) {
099        _constantValue = constantValue;
100    }
101
102    public String getConstantValue() {
103        return _constantValue;
104    }
105
106    public void setRegEx(String regEx) {
107        _regEx = regEx;
108    }
109
110    public String getRegEx() {
111        return _regEx;
112    }
113
114    public void setListenToOtherMemory(boolean listenToOtherMemory) {
115        this._listenToOtherMemory = listenToOtherMemory;
116    }
117
118    public boolean getListenToOtherMemory() {
119        return _listenToOtherMemory;
120    }
121
122    public void setMemoryOperation(MemoryOperation memoryOperation) {
123        _memoryOperation = memoryOperation;
124    }
125
126    public MemoryOperation getMemoryOperation() {
127        return _memoryOperation;
128    }
129
130    public void setCompareType(CompareType compareType) {
131        _compareType = compareType;
132    }
133
134    public CompareType getCompareType() {
135        return _compareType;
136    }
137
138    public void setCompareTo(CompareTo compareTo) {
139        _compareTo = compareTo;
140    }
141
142    public CompareTo getCompareTo() {
143        return _compareTo;
144    }
145
146    public void setCaseInsensitive(boolean caseInsensitive) {
147        _caseInsensitive = caseInsensitive;
148    }
149
150    public boolean getCaseInsensitive() {
151        return _caseInsensitive;
152    }
153
154    /** {@inheritDoc} */
155    @Override
156    public Category getCategory() {
157        return Category.ITEM;
158    }
159
160    private String getString(Object o) {
161        if (o != null) {
162            return o.toString();
163        }
164        return null;
165    }
166
167    private boolean matchRegex(String memoryValue, String regex) {
168        Pattern pattern = Pattern.compile(regex);
169        Matcher m = pattern.matcher(memoryValue);
170        return m.matches();
171    }
172
173    /** {@inheritDoc} */
174    @Override
175    public boolean evaluate() throws JmriException {
176        Memory memory = _selectNamedBean.evaluateNamedBean(getConditionalNG());
177
178        if (memory == null) return false;
179
180        // ConditionalVariable, line 661:  boolean compare(String value1, String value2, boolean caseInsensitive) {
181        String memoryValue = getString(memory.getValue());
182        String otherValue = null;
183        boolean result;
184
185        switch (_compareTo) {
186            case Value:
187                otherValue = _constantValue;
188                break;
189            case Memory:
190                Memory otherMemory = _selectOtherMemoryNamedBean.evaluateNamedBean(getConditionalNG());
191                otherValue = getString(otherMemory.getValue());
192                break;
193            case Table:
194                otherValue = getString(_selectTable.evaluateTableData(getConditionalNG()));
195                break;
196            case LocalVariable:
197                otherValue = TypeConversionUtil.convertToString(getConditionalNG().getSymbolTable().getValue(_localVariable), false);
198                break;
199            case RegEx:
200                // Do nothing
201                break;
202            default:
203                throw new IllegalArgumentException("_compareTo has unknown value: "+_compareTo.name());
204        }
205
206        switch (_memoryOperation) {
207            case LessThan:
208                // fall through
209            case LessThanOrEqual:
210                // fall through
211            case Equal:
212                // fall through
213            case NotEqual:
214                // fall through
215            case GreaterThanOrEqual:
216                // fall through
217            case GreaterThan:
218                result = CompareUtil.compare(_compareType, _memoryOperation._oper, memoryValue, otherValue, _caseInsensitive);
219                break;
220
221            case IsNull:
222                result = memoryValue == null;
223                break;
224            case IsNotNull:
225                result = memoryValue != null;
226                break;
227
228            case MatchRegex:
229                result = matchRegex(memoryValue, _regEx);
230                break;
231
232            case NotMatchRegex:
233                result = !matchRegex(memoryValue, _regEx);
234                break;
235
236            default:
237                throw new IllegalArgumentException("_memoryOperation has unknown value: "+_memoryOperation.name());
238        }
239
240        return result;
241    }
242
243    @Override
244    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
245        throw new UnsupportedOperationException("Not supported.");
246    }
247
248    @Override
249    public int getChildCount() {
250        return 0;
251    }
252
253    @Override
254    public String getShortDescription(Locale locale) {
255        return Bundle.getMessage(locale, "Memory_Short");
256    }
257
258    @Override
259    public String getLongDescription(Locale locale) {
260        String memoryName = _selectNamedBean.getDescription(locale);
261
262        String otherMemoryName = _selectOtherMemoryNamedBean.getDescription(locale);
263
264        String message;
265        String other1;
266        String other2 = null;
267        String other3 = null;
268
269        switch (_compareTo) {
270            case Value:
271                message = "Memory_Long_CompareConstant";
272                other1 = _constantValue;
273                break;
274
275            case Memory:
276                message = "Memory_Long_CompareMemory";
277                other1 = otherMemoryName;
278                break;
279
280            case Table:
281                message = "Memory_Long_CompareTable";
282                other1 = _selectTable.getTableNameDescription(locale);
283                other2 = _selectTable.getTableRowDescription(locale);
284                other3 = _selectTable.getTableColumnDescription(locale);
285                break;
286
287            case LocalVariable:
288                message = "Memory_Long_CompareLocalVariable";
289                other1 = _localVariable;
290                break;
291
292            case RegEx:
293                message = "Memory_Long_CompareRegEx";
294                other1 = _regEx;
295                break;
296
297            default:
298                throw new IllegalArgumentException("_compareTo has unknown value: "+_compareTo.name());
299        }
300
301        switch (_memoryOperation) {
302            case LessThan:
303                // fall through
304            case LessThanOrEqual:
305                // fall through
306            case Equal:
307                // fall through
308            case NotEqual:
309                // fall through
310            case GreaterThanOrEqual:
311                // fall through
312            case GreaterThan:
313                return Bundle.getMessage(locale, message, memoryName, _memoryOperation._text, other1, other2, other3);
314
315            case IsNull:
316                // fall through
317            case IsNotNull:
318                return Bundle.getMessage(locale, "Memory_Long_CompareNull", memoryName, _memoryOperation._text);
319
320            case MatchRegex:
321                // fall through
322            case NotMatchRegex:
323                return Bundle.getMessage(locale, "Memory_Long_CompareRegEx", memoryName, _memoryOperation._text, other1);
324
325            default:
326                throw new IllegalArgumentException("_memoryOperation has unknown value: "+_memoryOperation.name());
327        }
328    }
329
330    /** {@inheritDoc} */
331    @Override
332    public void setup() {
333        // Do nothing
334    }
335
336    /** {@inheritDoc} */
337    @Override
338    public void registerListenersForThisClass() {
339        if (!_listenersAreRegistered) {
340            _selectNamedBean.addPropertyChangeListener("value", this);
341            if (_listenToOtherMemory) {
342                _selectOtherMemoryNamedBean.addPropertyChangeListener("value", this);
343            }
344            _selectNamedBean.registerListeners();
345            _listenersAreRegistered = true;
346        }
347    }
348
349    /** {@inheritDoc} */
350    @Override
351    public void unregisterListenersForThisClass() {
352        if (_listenersAreRegistered) {
353            _selectNamedBean.removePropertyChangeListener("value", this);
354            if (_listenToOtherMemory) {
355                _selectOtherMemoryNamedBean.removePropertyChangeListener("value", this);
356            }
357            _selectNamedBean.unregisterListeners();
358            _listenersAreRegistered = false;
359        }
360    }
361
362    /** {@inheritDoc} */
363    @Override
364    public void propertyChange(PropertyChangeEvent evt) {
365        getConditionalNG().execute();
366    }
367
368    /** {@inheritDoc} */
369    @Override
370    public void disposeMe() {
371    }
372
373
374
375    public enum MemoryOperation {
376        LessThan(CompareOperation.LessThan, null, true),
377        LessThanOrEqual(CompareOperation.LessThanOrEqual, null, true),
378        Equal(CompareOperation.Equal, null, true),
379        GreaterThanOrEqual(CompareOperation.GreaterThanOrEqual, null, true),
380        GreaterThan(CompareOperation.GreaterThan, null, true),
381        NotEqual(CompareOperation.NotEqual, null, true),
382        IsNull(null, Bundle.getMessage("MemoryOperation_IsNull"), false),
383        IsNotNull(null, Bundle.getMessage("MemoryOperation_IsNotNull"), false),
384        MatchRegex(null, Bundle.getMessage("MemoryOperation_MatchRegEx"), true),
385        NotMatchRegex(null, Bundle.getMessage("MemoryOperation_NotMatchRegEx"), true);
386
387        private final CompareOperation _oper;
388        private final String _text;
389        private final boolean _extraValue;
390
391        private MemoryOperation(CompareOperation oper, String text, boolean extraValue) {
392            this._oper = oper;
393            this._text = oper != null ? oper.toString() : text;
394            this._extraValue = extraValue;
395        }
396
397        @Override
398        public String toString() {
399            return _text;
400        }
401
402        public boolean hasExtraValue() {
403            return _extraValue;
404        }
405
406    }
407
408
409    public enum CompareTo {
410        Value(Bundle.getMessage("Memory_CompareTo_Value")),
411        Memory(Bundle.getMessage("Memory_CompareTo_Memory")),
412        LocalVariable(Bundle.getMessage("Memory_CompareTo_LocalVariable")),
413        Table(Bundle.getMessage("Memory_CompareTo_Table")),
414        RegEx(Bundle.getMessage("Memory_CompareTo_RegularExpression"));
415
416        private final String _text;
417
418        private CompareTo(String text) {
419            this._text = text;
420        }
421
422        @Override
423        public String toString() {
424            return _text;
425        }
426
427    }
428
429    /** {@inheritDoc} */
430    @Override
431    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
432        log.debug("getUsageReport :: ExpressionMemory: bean = {}, report = {}", cdl, report);
433        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression);
434        _selectOtherMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression);
435    }
436
437    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionMemory.class);
438
439}