001package jmri.jmrit.display.logixng; 002 003import java.beans.*; 004import java.util.*; 005 006import javax.annotation.CheckForNull; 007import javax.annotation.Nonnull; 008 009import jmri.*; 010import jmri.jmrit.display.EditorManager; 011import static jmri.jmrit.display.EditorManager.EDITORS; 012import jmri.jmrit.display.layoutEditor.LayoutEditor; 013import jmri.jmrit.display.layoutEditor.LayoutTurnout; 014import jmri.jmrit.logixng.*; 015import jmri.jmrit.logixng.actions.AbstractDigitalAction; 016import jmri.jmrit.logixng.util.ReferenceUtil; 017import jmri.jmrit.logixng.util.parser.*; 018import jmri.jmrit.logixng.util.parser.ExpressionNode; 019import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 020import jmri.util.ThreadingUtil; 021import jmri.util.TypeConversionUtil; 022 023/** 024 * This action controls various things of a LayoutTurnout on a LayoutEditor panel. 025 * 026 * @author Daniel Bergqvist Copyright 2022 027 */ 028public class ActionLayoutTurnout extends AbstractDigitalAction 029 implements PropertyChangeListener, VetoableChangeListener { 030 031 private String _layoutEditorName; 032 private LayoutEditor _layoutEditor; 033 private NamedBeanAddressing _addressing = NamedBeanAddressing.Direct; 034 private String _layoutTurnoutName; 035 private LayoutTurnout _layoutTurnout; 036 private String _reference = ""; 037 private String _localVariable = ""; 038 private String _formula = ""; 039 private ExpressionNode _expressionNode; 040 private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct; 041 private Operation _operation = Operation.Enable; 042 private String _stateReference = ""; 043 private String _stateLocalVariable = ""; 044 private String _stateFormula = ""; 045 private ExpressionNode _stateExpressionNode; 046 047 public ActionLayoutTurnout(String sys, String user) 048 throws BadUserNameException, BadSystemNameException { 049 super(sys, user); 050 } 051 052 @Override 053 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 054 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 055 String sysName = systemNames.get(getSystemName()); 056 String userName = userNames.get(getSystemName()); 057 if (sysName == null) sysName = manager.getAutoSystemName(); 058 ActionLayoutTurnout copy = new ActionLayoutTurnout(sysName, userName); 059 copy.setComment(getComment()); 060 if (_layoutEditor != null) { 061 copy.setLayoutEditor(_layoutEditor.getName()); 062 } 063 copy.setLayoutTurnout(_layoutTurnout); 064 copy.setOperation(_operation); 065 copy.setAddressing(_addressing); 066 copy.setFormula(_formula); 067 copy.setLocalVariable(_localVariable); 068 copy.setReference(_reference); 069 copy.setStateAddressing(_stateAddressing); 070 copy.setStateFormula(_stateFormula); 071 copy.setStateLocalVariable(_stateLocalVariable); 072 copy.setStateReference(_stateReference); 073 return manager.registerAction(copy); 074 } 075 076 public void setLayoutEditor(@CheckForNull String layoutEditorName) { 077 assertListenersAreNotRegistered(log, "setEditor"); 078 079 InstanceManager.getDefault(EditorManager.class) 080 .removePropertyChangeListener(EDITORS, this); 081 082 _layoutEditorName = layoutEditorName; 083 084 if (layoutEditorName != null) { 085 _layoutEditor = InstanceManager.getDefault(EditorManager.class) 086 .get(LayoutEditor.class, layoutEditorName); 087 } else { 088 _layoutEditor = null; 089 } 090 if (_layoutEditor != null) { 091 InstanceManager.getDefault(EditorManager.class) 092 .addPropertyChangeListener(EDITORS, this); 093 } else { 094 _layoutTurnout = null; 095 } 096// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 097 } 098 099 public String getLayoutEditorName() { 100 if (_layoutEditor != null) { 101 return _layoutEditor.getName(); 102 } else { 103 return null; 104 } 105 } 106 107 public LayoutTurnout findLayoutTurnout(String name) { 108 if (_layoutEditor != null) { 109 for (LayoutTurnout lt : _layoutEditor.getLayoutTurnouts()) { 110 String turnoutName = lt.getTurnoutName(); 111 if (!turnoutName.isBlank() && name.equals(turnoutName)) { 112 return lt; 113 } 114 } 115 } 116 return null; 117 } 118 119 public LayoutTurnout findLayoutTurnout(jmri.Turnout turnout) { 120 if (_layoutEditor != null) { 121 for (LayoutTurnout lt : _layoutEditor.getLayoutTurnouts()) { 122 String turnoutName = lt.getTurnoutName(); 123 if (!turnoutName.isBlank() 124 && (turnoutName.equals(turnout.getSystemName()) 125 || turnoutName.equals(turnout.getUserName()))) { 126 return lt; 127 } 128 } 129 } 130 return null; 131 } 132 133 public void setLayoutTurnout(@CheckForNull String layoutTurnoutName) { 134 assertListenersAreNotRegistered(log, "setLayoutTurnout"); 135 _layoutTurnoutName = layoutTurnoutName; 136 if ((layoutTurnoutName != null) && (_layoutEditor != null)) { 137 this._layoutTurnout = findLayoutTurnout(layoutTurnoutName); 138 } else { 139 this._layoutTurnout = null; 140 } 141// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 142 } 143 144 public void setLayoutTurnout(@CheckForNull LayoutTurnout layoutTurnout) { 145 assertListenersAreNotRegistered(log, "setLayoutTurnout"); 146 if ((layoutTurnout != null) && (_layoutEditor != null)) { 147 this._layoutTurnout = layoutTurnout; 148 } else { 149 this._layoutTurnout = null; 150 } 151// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 152 } 153 154 public LayoutTurnout getLayoutTurnout() { 155 return _layoutTurnout; 156 } 157 158 public void setAddressing(NamedBeanAddressing addressing) throws ParserException { 159 _addressing = addressing; 160 parseFormula(); 161 } 162 163 public NamedBeanAddressing getAddressing() { 164 return _addressing; 165 } 166 167 public void setReference(@Nonnull String reference) { 168 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 169 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 170 } 171 _reference = reference; 172 } 173 174 public String getReference() { 175 return _reference; 176 } 177 178 public void setLocalVariable(@Nonnull String localVariable) { 179 _localVariable = localVariable; 180 } 181 182 public String getLocalVariable() { 183 return _localVariable; 184 } 185 186 public void setFormula(@Nonnull String formula) throws ParserException { 187 _formula = formula; 188 parseFormula(); 189 } 190 191 public String getFormula() { 192 return _formula; 193 } 194 195 private void parseFormula() throws ParserException { 196 if (_addressing == NamedBeanAddressing.Formula) { 197 Map<String, Variable> variables = new HashMap<>(); 198 199 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 200 _expressionNode = parser.parseExpression(_formula); 201 } else { 202 _expressionNode = null; 203 } 204 } 205 206 public void setStateAddressing(NamedBeanAddressing addressing) throws ParserException { 207 _stateAddressing = addressing; 208 parseStateFormula(); 209 } 210 211 public NamedBeanAddressing getStateAddressing() { 212 return _stateAddressing; 213 } 214 215 public void setOperation(Operation isControlling) { 216 _operation = isControlling; 217 } 218 219 public Operation getOperation() { 220 return _operation; 221 } 222 223 public void setStateReference(@Nonnull String reference) { 224 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 225 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 226 } 227 _stateReference = reference; 228 } 229 230 public String getStateReference() { 231 return _stateReference; 232 } 233 234 public void setStateLocalVariable(@Nonnull String localVariable) { 235 _stateLocalVariable = localVariable; 236 } 237 238 public String getStateLocalVariable() { 239 return _stateLocalVariable; 240 } 241 242 public void setStateFormula(@Nonnull String formula) throws ParserException { 243 _stateFormula = formula; 244 parseStateFormula(); 245 } 246 247 public String getStateFormula() { 248 return _stateFormula; 249 } 250 251 private void parseStateFormula() throws ParserException { 252 if (_stateAddressing == NamedBeanAddressing.Formula) { 253 Map<String, Variable> variables = new HashMap<>(); 254 255 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 256 _stateExpressionNode = parser.parseExpression(_stateFormula); 257 } else { 258 _stateExpressionNode = null; 259 } 260 } 261 262 @Override 263 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 264/* 265 if ("CanDelete".equals(evt.getPropertyName())) { // No I18N 266 if (evt.getOldValue() instanceof Turnout) { 267 if (evt.getOldValue().equals(getTurnout().getBean())) { 268 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 269 throw new PropertyVetoException(Bundle.getMessage("Turnout_TurnoutInUseTurnoutExpressionVeto", getDisplayName()), e); // NOI18N 270 } 271 } 272 } else if ("DoDelete".equals(evt.getPropertyName())) { // No I18N 273 if (evt.getOldValue() instanceof Turnout) { 274 if (evt.getOldValue().equals(getTurnout().getBean())) { 275 removeTurnout(); 276 } 277 } 278 } 279*/ 280 } 281 282 /** {@inheritDoc} */ 283 @Override 284 public Category getCategory() { 285 return CategoryDisplay.DISPLAY; 286 } 287 288 private String getNewState() throws JmriException { 289 290 switch (_stateAddressing) { 291 case Reference: 292 return ReferenceUtil.getReference( 293 getConditionalNG().getSymbolTable(), _stateReference); 294 295 case LocalVariable: 296 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 297 return TypeConversionUtil 298 .convertToString(symbolTable.getValue(_stateLocalVariable), false); 299 300 case Formula: 301 return _stateExpressionNode != null 302 ? TypeConversionUtil.convertToString( 303 _stateExpressionNode.calculate( 304 getConditionalNG().getSymbolTable()), false) 305 : null; 306 307 default: 308 throw new IllegalArgumentException("invalid _addressing state: " + _stateAddressing.name()); 309 } 310 } 311 312 /** {@inheritDoc} */ 313 @Override 314 public void execute() throws JmriException { 315 LayoutTurnout layoutTurnout; 316 317// System.out.format("ActionLayoutTurnout.execute: %s%n", getLongDescription()); 318 319 Object value; 320 321 switch (_addressing) { 322 case Direct: 323 layoutTurnout = this._layoutTurnout; 324 break; 325 326 case Reference: 327 String ref = ReferenceUtil.getReference( 328 getConditionalNG().getSymbolTable(), _reference); 329 layoutTurnout = findLayoutTurnout(ref); 330 break; 331 332 case LocalVariable: 333 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 334 value = symbolTable.getValue(_localVariable); 335 if (value instanceof jmri.Turnout) { 336 layoutTurnout = findLayoutTurnout((jmri.Turnout)value); 337 } else { 338 layoutTurnout = findLayoutTurnout(TypeConversionUtil 339 .convertToString(value, false)); 340 } 341 break; 342 343 case Formula: 344 if (_expressionNode != null) { 345 value = _expressionNode.calculate(getConditionalNG().getSymbolTable()); 346 if (value instanceof jmri.Turnout) { 347 layoutTurnout = findLayoutTurnout((jmri.Turnout)value); 348 } else { 349 layoutTurnout = findLayoutTurnout(TypeConversionUtil 350 .convertToString(value, false)); 351 } 352 } else { 353 layoutTurnout = null; 354 } 355 break; 356 357 default: 358 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 359 } 360 361// System.out.format("ActionLayoutTurnout.execute: layoutTurnout: %s%n", layoutTurnout); 362 363 if (layoutTurnout == null) { 364 log.debug("layoutTurnout is null"); 365 return; 366 } 367 368 String name = (_stateAddressing != NamedBeanAddressing.Direct) 369 ? getNewState() : null; 370 371 Operation operation; 372 if ((_stateAddressing == NamedBeanAddressing.Direct)) { 373 operation = _operation; 374 } else { 375 operation = Operation.valueOf(name); 376 } 377 378 ThreadingUtil.runOnGUI(() -> { 379 switch (operation) { 380 case Disable: 381 layoutTurnout.setDisabled(true); 382 break; 383 case Enable: 384 layoutTurnout.setDisabled(false); 385 break; 386/* 387 case Hide: 388 layoutTurnout.setHidden(true); 389 break; 390 case Show: 391 layoutTurnout.setHidden(false); 392 break; 393*/ 394 default: 395 throw new RuntimeException("operation has invalid value: "+operation.name()); 396 } 397 }); 398 } 399 400 @Override 401 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 402 throw new UnsupportedOperationException("Not supported."); 403 } 404 405 @Override 406 public int getChildCount() { 407 return 0; 408 } 409 410 @Override 411 public String getShortDescription(Locale locale) { 412 return Bundle.getMessage(locale, "ActionLayoutTurnout_Short"); 413 } 414 415 @Override 416 public String getLongDescription(Locale locale) { 417 String editorName = _layoutEditor != null 418 ? _layoutEditor.getName() : Bundle.getMessage(locale, "BeanNotSelected"); 419 String positonableName; 420 String state; 421 422 switch (_addressing) { 423 case Direct: 424 String layoutTurnoutName; 425 if (this._layoutTurnout != null) { 426 layoutTurnoutName = this._layoutTurnout.getTurnoutName(); 427 } else { 428 layoutTurnoutName = Bundle.getMessage(locale, "BeanNotSelected"); 429 } 430 positonableName = Bundle.getMessage(locale, "AddressByDirect", layoutTurnoutName); 431 break; 432 433 case Reference: 434 positonableName = Bundle.getMessage(locale, "AddressByReference", _reference); 435 break; 436 437 case LocalVariable: 438 positonableName = Bundle.getMessage(locale, "AddressByLocalVariable", _localVariable); 439 break; 440 441 case Formula: 442 positonableName = Bundle.getMessage(locale, "AddressByFormula", _formula); 443 break; 444 445 default: 446 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 447 } 448 449 switch (_stateAddressing) { 450 case Direct: 451 state = Bundle.getMessage(locale, "AddressByDirect", _operation._text); 452 break; 453 454 case Reference: 455 state = Bundle.getMessage(locale, "AddressByReference", _stateReference); 456 break; 457 458 case LocalVariable: 459 state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable); 460 break; 461 462 case Formula: 463 state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula); 464 break; 465 466 default: 467 throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name()); 468 } 469 470 return Bundle.getMessage(locale, "ActionLayoutTurnout_Long", editorName, positonableName, state); 471 } 472 473 /** {@inheritDoc} */ 474 @Override 475 public void setup() { 476 if ((_layoutEditorName != null) && (_layoutEditor == null)) { 477 setLayoutEditor(_layoutEditorName); 478 } 479 if ((_layoutTurnoutName != null) && (_layoutTurnout == null)) { 480 setLayoutTurnout(_layoutTurnoutName); 481 } 482 } 483 484 /** {@inheritDoc} */ 485 @Override 486 public void registerListenersForThisClass() { 487 } 488 489 /** {@inheritDoc} */ 490 @Override 491 public void unregisterListenersForThisClass() { 492 } 493 494 /** {@inheritDoc} */ 495 @Override 496 public void propertyChange(PropertyChangeEvent evt) { 497 if (EDITORS.equals(evt.getPropertyName())) { 498 if (evt.getOldValue() == _layoutEditor) { 499 _layoutEditor = null; 500 _layoutTurnout = null; 501 InstanceManager.getDefault(EditorManager.class) 502 .removePropertyChangeListener(EDITORS, this); 503 } 504 } 505 } 506 507 /** {@inheritDoc} */ 508 @Override 509 public void disposeMe() { 510 InstanceManager.getDefault(EditorManager.class) 511 .removePropertyChangeListener(EDITORS, this); 512 } 513 514 515 public enum Operation { 516 Disable(Bundle.getMessage("ActionLayoutTurnout_Disable")), 517 Enable(Bundle.getMessage("ActionLayoutTurnout_Enable")); 518// Hide(Bundle.getMessage("ActionLayoutTurnout_Hide")), 519// Show(Bundle.getMessage("ActionLayoutTurnout_Show")); 520 521 private final String _text; 522 523 private Operation(String text) { 524 this._text = text; 525 } 526 527 @Override 528 public String toString() { 529 return _text; 530 } 531 532 } 533 534 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionLayoutTurnout.class); 535 536}