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)); 031 032 private final LogixNG_SelectNamedBean<Memory> _selectOtherMemoryNamedBean = 033 new LogixNG_SelectNamedBean<>( 034 this, Memory.class, InstanceManager.getDefault(MemoryManager.class)); 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 LogixNG_Category getCategory() { 157 return LogixNG_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 _listenersAreRegistered = true; 345 } 346 } 347 348 /** {@inheritDoc} */ 349 @Override 350 public void unregisterListenersForThisClass() { 351 if (_listenersAreRegistered) { 352 _selectNamedBean.removePropertyChangeListener("value", this); 353 if (_listenToOtherMemory) { 354 _selectOtherMemoryNamedBean.removePropertyChangeListener("value", this); 355 } 356 _listenersAreRegistered = false; 357 } 358 } 359 360 /** {@inheritDoc} */ 361 @Override 362 public void propertyChange(PropertyChangeEvent evt) { 363 getConditionalNG().execute(); 364 } 365 366 /** {@inheritDoc} */ 367 @Override 368 public void disposeMe() { 369 } 370 371 372 373 public enum MemoryOperation { 374 LessThan(CompareOperation.LessThan, null, true), 375 LessThanOrEqual(CompareOperation.LessThanOrEqual, null, true), 376 Equal(CompareOperation.Equal, null, true), 377 GreaterThanOrEqual(CompareOperation.GreaterThanOrEqual, null, true), 378 GreaterThan(CompareOperation.GreaterThan, null, true), 379 NotEqual(CompareOperation.NotEqual, null, true), 380 IsNull(null, Bundle.getMessage("MemoryOperation_IsNull"), false), 381 IsNotNull(null, Bundle.getMessage("MemoryOperation_IsNotNull"), false), 382 MatchRegex(null, Bundle.getMessage("MemoryOperation_MatchRegEx"), true), 383 NotMatchRegex(null, Bundle.getMessage("MemoryOperation_NotMatchRegEx"), true); 384 385 private final CompareOperation _oper; 386 private final String _text; 387 private final boolean _extraValue; 388 389 private MemoryOperation(CompareOperation oper, String text, boolean extraValue) { 390 this._oper = oper; 391 this._text = oper != null ? oper.toString() : text; 392 this._extraValue = extraValue; 393 } 394 395 @Override 396 public String toString() { 397 return _text; 398 } 399 400 public boolean hasExtraValue() { 401 return _extraValue; 402 } 403 404 } 405 406 407 public enum CompareTo { 408 Value(Bundle.getMessage("Memory_CompareTo_Value")), 409 Memory(Bundle.getMessage("Memory_CompareTo_Memory")), 410 LocalVariable(Bundle.getMessage("Memory_CompareTo_LocalVariable")), 411 Table(Bundle.getMessage("Memory_CompareTo_Table")), 412 RegEx(Bundle.getMessage("Memory_CompareTo_RegularExpression")); 413 414 private final String _text; 415 416 private CompareTo(String text) { 417 this._text = text; 418 } 419 420 @Override 421 public String toString() { 422 return _text; 423 } 424 425 } 426 427 /** {@inheritDoc} */ 428 @Override 429 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 430 log.debug("getUsageReport :: ExpressionMemory: bean = {}, report = {}", cdl, report); 431 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression); 432 _selectOtherMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression); 433 } 434 435 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionMemory.class); 436 437}