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