001package jmri.jmrit.logixng.actions; 002 003import java.beans.*; 004import java.util.*; 005import java.util.concurrent.atomic.AtomicReference; 006 007import javax.annotation.Nonnull; 008 009import jmri.*; 010import jmri.jmrit.logixng.*; 011import jmri.jmrit.logixng.util.parser.*; 012import jmri.jmrit.logixng.util.parser.ExpressionNode; 013import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 014import jmri.jmrit.logixng.util.LogixNG_SelectTable; 015import jmri.jmrit.logixng.util.ReferenceUtil; 016import jmri.util.ThreadingUtil; 017import jmri.util.TypeConversionUtil; 018 019/** 020 * This action sets the value of a local variable. 021 * 022 * @author Daniel Bergqvist Copyright 2020 023 */ 024public class ActionLocalVariable extends AbstractDigitalAction 025 implements PropertyChangeListener { 026 027 private String _localVariable; 028 029 private final LogixNG_SelectNamedBean<Memory> _selectMemoryNamedBean = 030 new LogixNG_SelectNamedBean<>( 031 this, Memory.class, InstanceManager.getDefault(MemoryManager.class), this); 032 033 private final LogixNG_SelectNamedBean<Block> _selectBlockNamedBean = 034 new LogixNG_SelectNamedBean<>( 035 this, Block.class, InstanceManager.getDefault(BlockManager.class), this); 036 037 private final LogixNG_SelectNamedBean<Reporter> _selectReporterNamedBean = 038 new LogixNG_SelectNamedBean<>( 039 this, Reporter.class, InstanceManager.getDefault(ReporterManager.class), this); 040 041 private VariableOperation _variableOperation = VariableOperation.SetToString; 042 private ConstantType _constantType = ConstantType.String; 043 private String _constantValue = ""; 044 private String _otherLocalVariable = ""; 045 private String _reference = ""; 046 private String _formula = ""; 047 private ExpressionNode _expressionNode; 048 private boolean _listenToMemory = false; 049 private boolean _listenToBlock = false; 050 private boolean _listenToReporter = false; 051 052 private final LogixNG_SelectTable _selectTable = 053 new LogixNG_SelectTable(this, () -> {return _variableOperation == VariableOperation.CopyTableCellToVariable;}); 054 055 056 public ActionLocalVariable(String sys, String user) 057 throws BadUserNameException, BadSystemNameException { 058 super(sys, user); 059 060 _selectMemoryNamedBean.setOnlyDirectAddressingAllowed(); 061 _selectBlockNamedBean.setOnlyDirectAddressingAllowed(); 062 _selectReporterNamedBean.setOnlyDirectAddressingAllowed(); 063 } 064 065 @Override 066 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 067 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 068 String sysName = systemNames.get(getSystemName()); 069 String userName = userNames.get(getSystemName()); 070 if (sysName == null) sysName = manager.getAutoSystemName(); 071 ActionLocalVariable copy = new ActionLocalVariable(sysName, userName); 072 copy.setComment(getComment()); 073 copy.setLocalVariable(_localVariable); 074 copy.setVariableOperation(_variableOperation); 075 copy.setConstantType(_constantType); 076 copy.setConstantValue(_constantValue); 077 _selectMemoryNamedBean.copy(copy._selectMemoryNamedBean); 078 _selectBlockNamedBean.copy(copy._selectBlockNamedBean); 079 _selectReporterNamedBean.copy(copy._selectReporterNamedBean); 080 copy.setOtherLocalVariable(_otherLocalVariable); 081 copy.setReference(_reference); 082 copy.setFormula(_formula); 083 _selectTable.copy(copy._selectTable); 084 copy.setListenToMemory(_listenToMemory); 085 copy.setListenToBlock(_listenToBlock); 086 copy.setListenToReporter(_listenToReporter); 087 return manager.registerAction(copy); 088 } 089 090 public void setLocalVariable(String variableName) { 091 assertListenersAreNotRegistered(log, "setLocalVariable"); // No I18N 092 _localVariable = variableName; 093 } 094 095 public String getLocalVariable() { 096 return _localVariable; 097 } 098 099 public LogixNG_SelectNamedBean<Memory> getSelectMemoryNamedBean() { 100 return _selectMemoryNamedBean; 101 } 102 103 public LogixNG_SelectNamedBean<Block> getSelectBlockNamedBean() { 104 return _selectBlockNamedBean; 105 } 106 107 public LogixNG_SelectNamedBean<Reporter> getSelectReporterNamedBean() { 108 return _selectReporterNamedBean; 109 } 110 111 public void setVariableOperation(VariableOperation variableOperation) throws ParserException { 112 _variableOperation = variableOperation; 113 parseFormula(); 114 } 115 116 public VariableOperation getVariableOperation() { 117 return _variableOperation; 118 } 119 120 public LogixNG_SelectTable getSelectTable() { 121 return _selectTable; 122 } 123 124 public void setOtherLocalVariable(@Nonnull String localVariable) { 125 assertListenersAreNotRegistered(log, "setOtherLocalVariable"); 126 _otherLocalVariable = localVariable; 127 } 128 129 public String getOtherLocalVariable() { 130 return _otherLocalVariable; 131 } 132 133 public void setReference(@Nonnull String reference) { 134 assertListenersAreNotRegistered(log, "setReference"); 135 _reference = reference; 136 } 137 138 public String getReference() { 139 return _reference; 140 } 141 142 public void setConstantType(ConstantType constantType) { 143 _constantType = constantType; 144 } 145 146 public ConstantType getConstantType() { 147 return _constantType; 148 } 149 150 public void setConstantValue(String constantValue) { 151 _constantValue = constantValue; 152 } 153 154 public String getConstantValue() { 155 return _constantValue; 156 } 157 158 public void setFormula(String formula) throws ParserException { 159 _formula = formula; 160 parseFormula(); 161 } 162 163 public String getFormula() { 164 return _formula; 165 } 166 167 public void setListenToMemory(boolean listenToMemory) { 168 this._listenToMemory = listenToMemory; 169 } 170 171 public boolean getListenToMemory() { 172 return _listenToMemory; 173 } 174 175 public void setListenToBlock(boolean listenToBlock) { 176 this._listenToBlock = listenToBlock; 177 } 178 179 public boolean getListenToBlock() { 180 return _listenToBlock; 181 } 182 183 public void setListenToReporter(boolean listenToReporter) { 184 this._listenToReporter = listenToReporter; 185 } 186 187 public boolean getListenToReporter() { 188 return _listenToReporter; 189 } 190 191 private void parseFormula() throws ParserException { 192 if (_variableOperation == VariableOperation.CalculateFormula) { 193 Map<String, Variable> variables = new HashMap<>(); 194 195 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 196 _expressionNode = parser.parseExpression(_formula); 197 } else { 198 _expressionNode = null; 199 } 200 } 201 202 /** {@inheritDoc} */ 203 @Override 204 public Category getCategory() { 205 return Category.ITEM; 206 } 207 208 /** {@inheritDoc} */ 209 @Override 210 public void execute() throws JmriException { 211 if (_localVariable == null) return; 212 213 final ConditionalNG conditionalNG = getConditionalNG(); 214 215 SymbolTable symbolTable = conditionalNG.getSymbolTable(); 216 217 AtomicReference<JmriException> ref = new AtomicReference<>(); 218 219 ThreadingUtil.runOnLayoutWithJmriException(() -> { 220 221 switch (_variableOperation) { 222 case SetToNull: 223 symbolTable.setValue(_localVariable, null); 224 break; 225 226 case SetToString: { 227 Object value; 228 switch (_constantType) { 229 case String: 230 value = _constantValue; 231 break; 232 case Integer: 233 value = TypeConversionUtil.convertToLong(_constantValue); 234 break; 235 case FloatingNumber: 236 value = TypeConversionUtil.convertToDouble(_constantValue, true, true, true); 237 break; 238 case Boolean: 239 value = TypeConversionUtil.convertToBoolean(_constantValue, true); 240 break; 241 default: 242 // Throw exception 243 throw new IllegalArgumentException("_constantType has invalid value: {}" + _constantType.name()); 244 } 245 symbolTable.setValue(_localVariable, value); 246 break; 247 } 248 249 case CopyVariableToVariable: 250 Object variableValue = conditionalNG 251 .getSymbolTable().getValue(_otherLocalVariable); 252 253 symbolTable.setValue(_localVariable, variableValue); 254 break; 255 256 case CopyMemoryToVariable: 257 Memory memory = _selectMemoryNamedBean.evaluateNamedBean(conditionalNG); 258 if (memory != null) { 259 symbolTable.setValue(_localVariable, memory.getValue()); 260 } else { 261 log.warn("ActionLocalVariable should copy memory to variable but memory is null"); 262 } 263 break; 264 265 case CopyReferenceToVariable: 266 symbolTable.setValue(_localVariable, ReferenceUtil.getReference( 267 conditionalNG.getSymbolTable(), _reference)); 268 break; 269 270 case CopyTableCellToVariable: 271 Object value = _selectTable.evaluateTableData(conditionalNG); 272 symbolTable.setValue(_localVariable, value); 273 break; 274 275 case CopyBlockToVariable: 276 Block block = _selectBlockNamedBean.evaluateNamedBean(conditionalNG); 277 if (block != null) { 278 symbolTable.setValue(_localVariable, block.getValue()); 279 } else { 280 log.warn("ActionLocalVariable should copy block value to variable but block is null"); 281 } 282 break; 283 284 case CopyReporterToVariable: 285 Reporter reporter = _selectReporterNamedBean.evaluateNamedBean(conditionalNG); 286 if (reporter != null) { 287 symbolTable.setValue(_localVariable, reporter.getCurrentReport()); 288 } else { 289 log.warn("ActionLocalVariable should copy current report to variable but reporter is null"); 290 } 291 break; 292 293 case CalculateFormula: 294 if (_formula.isEmpty()) { 295 symbolTable.setValue(_localVariable, null); 296 } else { 297 if (_expressionNode == null) return; 298 299 symbolTable.setValue(_localVariable, 300 _expressionNode.calculate( 301 conditionalNG.getSymbolTable())); 302 } 303 break; 304 305 default: 306 // Throw exception 307 throw new IllegalArgumentException("_variableOperation has invalid value: {}" + _variableOperation.name()); 308 } 309 }); 310 311 if (ref.get() != null) throw ref.get(); 312 } 313 314 @Override 315 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 316 throw new UnsupportedOperationException("Not supported."); 317 } 318 319 @Override 320 public int getChildCount() { 321 return 0; 322 } 323 324 @Override 325 public String getShortDescription(Locale locale) { 326 return Bundle.getMessage(locale, "ActionLocalVariable_Short"); 327 } 328 329 @Override 330 public String getLongDescription(Locale locale) { 331 String copyToMemoryName = _selectMemoryNamedBean.getDescription(locale); 332 String copyToBlockName = _selectBlockNamedBean.getDescription(locale); 333 String copyToReporterName = _selectReporterNamedBean.getDescription(locale); 334 335 switch (_variableOperation) { 336 case SetToNull: 337 return Bundle.getMessage(locale, "ActionLocalVariable_Long_Null", _localVariable); 338 339 case SetToString: 340 return Bundle.getMessage(locale, "ActionLocalVariable_Long_Value", 341 _localVariable, _constantType._text, _constantValue); 342 343 case CopyVariableToVariable: 344 return Bundle.getMessage(locale, "ActionLocalVariable_Long_CopyVariableToVariable", 345 _localVariable, _otherLocalVariable); 346 347 case CopyMemoryToVariable: 348 return Bundle.getMessage(locale, "ActionLocalVariable_Long_CopyMemoryToVariable", 349 _localVariable, copyToMemoryName, Base.getListenString(_listenToMemory)); 350 351 case CopyReferenceToVariable: 352 return Bundle.getMessage(locale, "ActionLocalVariable_Long_CopyReferenceToVariable", 353 _localVariable, _reference); 354 355 case CopyBlockToVariable: 356 return Bundle.getMessage(locale, "ActionLocalVariable_Long_CopyBlockToVariable", 357 _localVariable, copyToBlockName, Base.getListenString(_listenToBlock)); 358 359 case CopyTableCellToVariable: 360 String tableName = _selectTable.getTableNameDescription(locale); 361 String rowName = _selectTable.getTableRowDescription(locale); 362 String columnName = _selectTable.getTableColumnDescription(locale); 363 return Bundle.getMessage(locale, "ActionLocalVariable_Long_CopyTableCellToVariable", _localVariable, tableName, rowName, columnName); 364 365 case CopyReporterToVariable: 366 return Bundle.getMessage(locale, "ActionLocalVariable_Long_CopyReporterToVariable", 367 _localVariable, copyToReporterName, Base.getListenString(_listenToReporter)); 368 369 case CalculateFormula: 370 return Bundle.getMessage(locale, "ActionLocalVariable_Long_Formula", _localVariable, _formula); 371 372 default: 373 throw new IllegalArgumentException("_variableOperation has invalid value: " + _variableOperation.name()); 374 } 375 } 376 377 /** {@inheritDoc} */ 378 @Override 379 public void setup() { 380 // Do nothing 381 } 382 383 /** {@inheritDoc} */ 384 @Override 385 public void registerListenersForThisClass() { 386 if (!_listenersAreRegistered) { 387 if (_listenToMemory 388 && (_variableOperation == VariableOperation.CopyMemoryToVariable)) { 389 _selectMemoryNamedBean.addPropertyChangeListener("value", this); 390 } 391 if (_listenToBlock 392 && (_variableOperation == VariableOperation.CopyBlockToVariable)) { 393 _selectBlockNamedBean.addPropertyChangeListener("value", this); 394 } 395 if (_listenToReporter 396 && (_variableOperation == VariableOperation.CopyReporterToVariable)) { 397 _selectReporterNamedBean.addPropertyChangeListener("currentReport", this); 398 } 399 _selectMemoryNamedBean.registerListeners(); 400 _selectBlockNamedBean.registerListeners(); 401 _selectReporterNamedBean.registerListeners(); 402 _listenersAreRegistered = true; 403 } 404 } 405 406 /** {@inheritDoc} */ 407 @Override 408 public void unregisterListenersForThisClass() { 409 if (_listenersAreRegistered) { 410 if (_listenToMemory 411 && (_variableOperation == VariableOperation.CopyMemoryToVariable)) { 412 _selectMemoryNamedBean.removePropertyChangeListener("value", this); 413 } 414 if (_listenToBlock 415 && (_variableOperation == VariableOperation.CopyBlockToVariable)) { 416 _selectBlockNamedBean.removePropertyChangeListener("value", this); 417 } 418 if (_listenToReporter 419 && (_variableOperation == VariableOperation.CopyReporterToVariable)) { 420 _selectReporterNamedBean.removePropertyChangeListener("currentReport", this); 421 } 422 _selectMemoryNamedBean.unregisterListeners(); 423 _selectBlockNamedBean.unregisterListeners(); 424 _selectReporterNamedBean.unregisterListeners(); 425 _listenersAreRegistered = false; 426 } 427 } 428 429 /** {@inheritDoc} */ 430 @Override 431 public void propertyChange(PropertyChangeEvent evt) { 432 getConditionalNG().execute(); 433 } 434 435 /** {@inheritDoc} */ 436 @Override 437 public void disposeMe() { 438 } 439 440 441 public enum VariableOperation { 442 SetToNull(Bundle.getMessage("ActionLocalVariable_VariableOperation_SetToNull")), 443 SetToString(Bundle.getMessage("ActionLocalVariable_VariableOperation_SetToString")), 444 CopyVariableToVariable(Bundle.getMessage("ActionLocalVariable_VariableOperation_CopyVariableToVariable")), 445 CopyMemoryToVariable(Bundle.getMessage("ActionLocalVariable_VariableOperation_CopyMemoryToVariable")), 446 CopyReferenceToVariable(Bundle.getMessage("ActionLocalVariable_VariableOperation_CopyReferenceToVariable")), 447 CopyTableCellToVariable(Bundle.getMessage("ActionLocalVariable_VariableOperation_CopyTableCellToVariable")), 448 CopyBlockToVariable(Bundle.getMessage("ActionLocalVariable_VariableOperation_CopyBlockToVariable")), 449 CopyReporterToVariable(Bundle.getMessage("ActionLocalVariable_VariableOperation_CopyReporterToVariable")), 450 CalculateFormula(Bundle.getMessage("ActionLocalVariable_VariableOperation_CalculateFormula")); 451 452 private final String _text; 453 454 private VariableOperation(String text) { 455 this._text = text; 456 } 457 458 @Override 459 public String toString() { 460 return _text; 461 } 462 463 } 464 465 public enum ConstantType { 466 String(Bundle.getMessage("ActionLocalVariable_ConstantType_String")), 467 Integer(Bundle.getMessage("ActionLocalVariable_ConstantType_Integer")), 468 FloatingNumber(Bundle.getMessage("ActionLocalVariable_ConstantType_FloatingNumber")), 469 Boolean(Bundle.getMessage("ActionLocalVariable_ConstantType_Boolean")); 470 471 private final String _text; 472 473 private ConstantType(String text) { 474 this._text = text; 475 } 476 477 @Override 478 public String toString() { 479 return _text; 480 } 481 482 } 483 484 /** {@inheritDoc} */ 485 @Override 486 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 487 log.debug("getUsageReport :: ActionLocalVariable: bean = {}, report = {}", cdl, report); 488 _selectMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 489 _selectBlockNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 490 _selectReporterNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 491 } 492 493 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionLocalVariable.class); 494 495}