001package jmri.jmrit.display.logixng; 002 003// import java.beans.PropertyChangeEvent; 004// import java.beans.PropertyVetoException; 005import java.beans.VetoableChangeListener; 006import java.util.*; 007 008import javax.annotation.CheckForNull; 009import javax.annotation.Nonnull; 010 011import jmri.*; 012import jmri.jmrit.display.Editor; 013import jmri.jmrit.display.Positionable; 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 Positionable on a panel. 025 * 026 * @author Daniel Bergqvist Copyright 2021 027 */ 028public class ActionPositionable extends AbstractDigitalAction implements VetoableChangeListener { 029 030 private String _editorName; 031 private Editor _editor; 032 private NamedBeanAddressing _addressing = NamedBeanAddressing.Direct; 033// private NamedBeanHandle<Turnout> _turnoutHandle; 034 private String _positionableName; 035 private Positionable _positionable; 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 ActionPositionable(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 ActionPositionable copy = new ActionPositionable(sysName, userName); 059 copy.setComment(getComment()); 060 copy.setEditor(_editorName); 061 copy.setPositionable(_positionableName); 062 copy.setOperation(_operation); 063 copy.setAddressing(_addressing); 064 copy.setFormula(_formula); 065 copy.setLocalVariable(_localVariable); 066 copy.setReference(_reference); 067 copy.setStateAddressing(_stateAddressing); 068 copy.setStateFormula(_stateFormula); 069 copy.setStateLocalVariable(_stateLocalVariable); 070 copy.setStateReference(_stateReference); 071 return manager.registerAction(copy); 072 } 073 074 public void setEditor(@CheckForNull String editorName) { 075 assertListenersAreNotRegistered(log, "setEditor"); 076 _editorName = editorName; 077 if (editorName != null) { 078 _editor = jmri.InstanceManager.getDefault(jmri.jmrit.display.EditorManager.class).getByName(editorName); 079 } else { 080 _editor = null; 081 } 082// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 083 } 084 085 public String getEditorName() { 086 return _editorName; 087 } 088 089 public void setPositionable(@CheckForNull String positionableName) { 090 assertListenersAreNotRegistered(log, "setPositionable"); 091 _positionableName = positionableName; 092 if ((positionableName != null) && (_editor != null)) { 093 _positionable = _editor.getIdContents().get(_positionableName); 094 } else { 095 _positionable = null; 096 } 097// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 098 } 099 100 public String getPositionableName() { 101 return _positionableName; 102 } 103 104 public void setAddressing(NamedBeanAddressing addressing) throws ParserException { 105 _addressing = addressing; 106 parseFormula(); 107 } 108 109 public NamedBeanAddressing getAddressing() { 110 return _addressing; 111 } 112 113 public void setReference(@Nonnull String reference) { 114 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 115 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 116 } 117 _reference = reference; 118 } 119 120 public String getReference() { 121 return _reference; 122 } 123 124 public void setLocalVariable(@Nonnull String localVariable) { 125 _localVariable = localVariable; 126 } 127 128 public String getLocalVariable() { 129 return _localVariable; 130 } 131 132 public void setFormula(@Nonnull String formula) throws ParserException { 133 _formula = formula; 134 parseFormula(); 135 } 136 137 public String getFormula() { 138 return _formula; 139 } 140 141 private void parseFormula() throws ParserException { 142 if (_addressing == NamedBeanAddressing.Formula) { 143 Map<String, Variable> variables = new HashMap<>(); 144 145 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 146 _expressionNode = parser.parseExpression(_formula); 147 } else { 148 _expressionNode = null; 149 } 150 } 151 152 public void setStateAddressing(NamedBeanAddressing addressing) throws ParserException { 153 _stateAddressing = addressing; 154 parseStateFormula(); 155 } 156 157 public NamedBeanAddressing getStateAddressing() { 158 return _stateAddressing; 159 } 160 161 public void setOperation(Operation isControlling) { 162 _operation = isControlling; 163 } 164 165 public Operation getOperation() { 166 return _operation; 167 } 168 169 public void setStateReference(@Nonnull String reference) { 170 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 171 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 172 } 173 _stateReference = reference; 174 } 175 176 public String getStateReference() { 177 return _stateReference; 178 } 179 180 public void setStateLocalVariable(@Nonnull String localVariable) { 181 _stateLocalVariable = localVariable; 182 } 183 184 public String getStateLocalVariable() { 185 return _stateLocalVariable; 186 } 187 188 public void setStateFormula(@Nonnull String formula) throws ParserException { 189 _stateFormula = formula; 190 parseStateFormula(); 191 } 192 193 public String getStateFormula() { 194 return _stateFormula; 195 } 196 197 private void parseStateFormula() throws ParserException { 198 if (_stateAddressing == NamedBeanAddressing.Formula) { 199 Map<String, Variable> variables = new HashMap<>(); 200 201 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 202 _stateExpressionNode = parser.parseExpression(_stateFormula); 203 } else { 204 _stateExpressionNode = null; 205 } 206 } 207 208 @Override 209 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 210/* 211 if ("CanDelete".equals(evt.getPropertyName())) { // No I18N 212 if (evt.getOldValue() instanceof Turnout) { 213 if (evt.getOldValue().equals(getTurnout().getBean())) { 214 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 215 throw new PropertyVetoException(Bundle.getMessage("Turnout_TurnoutInUseTurnoutExpressionVeto", getDisplayName()), e); // NOI18N 216 } 217 } 218 } else if ("DoDelete".equals(evt.getPropertyName())) { // No I18N 219 if (evt.getOldValue() instanceof Turnout) { 220 if (evt.getOldValue().equals(getTurnout().getBean())) { 221 removeTurnout(); 222 } 223 } 224 } 225*/ 226 } 227 228 /** {@inheritDoc} */ 229 @Override 230 public Category getCategory() { 231 return CategoryDisplay.DISPLAY; 232 } 233 234 private String getNewState() throws JmriException { 235 236 switch (_stateAddressing) { 237 case Reference: 238 return ReferenceUtil.getReference( 239 getConditionalNG().getSymbolTable(), _stateReference); 240 241 case LocalVariable: 242 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 243 return TypeConversionUtil 244 .convertToString(symbolTable.getValue(_stateLocalVariable), false); 245 246 case Formula: 247 return _stateExpressionNode != null 248 ? TypeConversionUtil.convertToString( 249 _stateExpressionNode.calculate( 250 getConditionalNG().getSymbolTable()), false) 251 : null; 252 253 default: 254 throw new IllegalArgumentException("invalid _addressing state: " + _stateAddressing.name()); 255 } 256 } 257 258 private void throwErrorPositionableDoesNotExists() throws JmriException { 259 var lng = getConditionalNG(); 260 var cng = getConditionalNG(); 261 var m = getModule(); 262 String errorMessage; 263 if (m != null) { 264 errorMessage = Bundle.getMessage( 265 "ActionPositionable_ErrorNoPositionable_Module", 266 getLongDescription(), m.getDisplayName(), getSystemName()); 267 } else { 268 errorMessage = Bundle.getMessage( 269 "ActionPositionable_ErrorNoPositionable_LogixNG", 270 getLongDescription(), lng.getDisplayName(), cng.getDisplayName(), getSystemName()); 271 } 272 List<String> list = Arrays.asList(errorMessage.split("\n")); 273 throw new JmriException(Bundle.getMessage("ActionPositionable_ErrorNoPositionable"), list); 274 } 275 276 /** {@inheritDoc} */ 277 @Override 278 public void execute() throws JmriException { 279 Positionable positionable; 280 281// System.out.format("ActionPositionable.execute: %s%n", getLongDescription()); 282 283 switch (_addressing) { 284 case Direct: 285 positionable = this._positionable; 286 if (positionable == null && (_positionableName != null && !_positionableName.isBlank())) { 287 throwErrorPositionableDoesNotExists(); 288 } 289 break; 290 291 case Reference: 292 String ref = ReferenceUtil.getReference( 293 getConditionalNG().getSymbolTable(), _reference); 294 positionable = _editor.getIdContents().get(ref); 295 break; 296 297 case LocalVariable: 298 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 299 positionable = _editor.getIdContents().get(TypeConversionUtil 300 .convertToString(symbolTable.getValue(_localVariable), false)); 301 break; 302 303 case Formula: 304 positionable = _expressionNode != null ? 305 _editor.getIdContents().get(TypeConversionUtil 306 .convertToString(_expressionNode.calculate( 307 getConditionalNG().getSymbolTable()), false)) 308 : null; 309 break; 310 311 default: 312 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 313 } 314 315// System.out.format("ActionPositionable.execute: positionable: %s%n", positionable); 316 317 if (positionable == null) { 318 log.error("positionable is null"); 319 return; 320 } 321 322 String name = (_stateAddressing != NamedBeanAddressing.Direct) 323 ? getNewState() : null; 324 325 Operation operation; 326 if ((_stateAddressing == NamedBeanAddressing.Direct)) { 327 operation = _operation; 328 } else { 329 operation = Operation.valueOf(name); 330 } 331 332 ThreadingUtil.runOnGUI(() -> { 333 switch (operation) { 334 case Disable: 335 positionable.setControlling(false); 336 break; 337 case Enable: 338 positionable.setControlling(true); 339 break; 340 case Hide: 341 positionable.setHidden(true); 342 break; 343 case Show: 344 positionable.setHidden(false); 345 break; 346 default: 347 throw new RuntimeException("operation has invalid value: "+operation.name()); 348 } 349 }); 350 } 351 352 @Override 353 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 354 throw new UnsupportedOperationException("Not supported."); 355 } 356 357 @Override 358 public int getChildCount() { 359 return 0; 360 } 361 362 @Override 363 public String getShortDescription(Locale locale) { 364 return Bundle.getMessage(locale, "ActionPositionable_Short"); 365 } 366 367 @Override 368 public String getLongDescription(Locale locale) { 369 String editorName = _editorName != null ? _editorName : Bundle.getMessage(locale, "BeanNotSelected"); 370 String positonableName; 371 String state; 372 373 switch (_addressing) { 374 case Direct: 375 String positionableName; 376 if (this._positionableName != null) { 377 positionableName = this._positionableName; 378 } else { 379 positionableName = Bundle.getMessage(locale, "BeanNotSelected"); 380 } 381 positonableName = Bundle.getMessage(locale, "AddressByDirect", positionableName); 382 break; 383 384 case Reference: 385 positonableName = Bundle.getMessage(locale, "AddressByReference", _reference); 386 break; 387 388 case LocalVariable: 389 positonableName = Bundle.getMessage(locale, "AddressByLocalVariable", _localVariable); 390 break; 391 392 case Formula: 393 positonableName = Bundle.getMessage(locale, "AddressByFormula", _formula); 394 break; 395 396 default: 397 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 398 } 399 400 switch (_stateAddressing) { 401 case Direct: 402 state = Bundle.getMessage(locale, "AddressByDirect", _operation._text); 403 break; 404 405 case Reference: 406 state = Bundle.getMessage(locale, "AddressByReference", _stateReference); 407 break; 408 409 case LocalVariable: 410 state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable); 411 break; 412 413 case Formula: 414 state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula); 415 break; 416 417 default: 418 throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name()); 419 } 420 421 return Bundle.getMessage(locale, "ActionPositionable_Long", editorName, positonableName, state); 422 } 423 424 /** {@inheritDoc} */ 425 @Override 426 public void setup() { 427 if ((_editorName != null) && (_editor == null)) { 428 setEditor(_editorName); 429 } 430 if ((_positionableName != null) && (_positionable == null)) { 431 setPositionable(_positionableName); 432 } 433 } 434 435 /** {@inheritDoc} */ 436 @Override 437 public void registerListenersForThisClass() { 438 } 439 440 /** {@inheritDoc} */ 441 @Override 442 public void unregisterListenersForThisClass() { 443 } 444 445 /** {@inheritDoc} */ 446 @Override 447 public void disposeMe() { 448 } 449 450 451 public enum Operation { 452 Disable(Bundle.getMessage("ActionPositionable_Disable")), 453 Enable(Bundle.getMessage("ActionPositionable_Enable")), 454 Hide(Bundle.getMessage("ActionPositionable_Hide")), 455 Show(Bundle.getMessage("ActionPositionable_Show")); 456 457 private final String _text; 458 459 private Operation(String text) { 460 this._text = text; 461 } 462 463 @Override 464 public String toString() { 465 return _text; 466 } 467 468 } 469 470 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionPositionable.class); 471 472}