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}