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.display.layoutEditor.LayoutEditor; 015import jmri.jmrit.logixng.*; 016import jmri.jmrit.logixng.actions.AbstractDigitalAction; 017import jmri.jmrit.logixng.util.ReferenceUtil; 018import jmri.jmrit.logixng.util.parser.*; 019import jmri.jmrit.logixng.util.parser.ExpressionNode; 020import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 021import jmri.util.ThreadingUtil; 022import jmri.util.TypeConversionUtil; 023 024/** 025 * This action controls various things of Positionables with a particular 026 * class name on a panel. 027 * 028 * @author Daniel Bergqvist Copyright 2023 029 */ 030public class ActionPositionableByClass extends AbstractDigitalAction implements VetoableChangeListener { 031 032 private String _editorName; 033 private Editor _editor; 034 private NamedBeanAddressing _addressing = NamedBeanAddressing.Direct; 035// private NamedBeanHandle<Turnout> _turnoutHandle; 036 private String _className; 037// private Positionable _positionable; 038 private String _reference = ""; 039 private String _localVariable = ""; 040 private String _formula = ""; 041 private ExpressionNode _expressionNode; 042 private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct; 043 private Operation _operation = Operation.Enable; 044 private String _stateReference = ""; 045 private String _stateLocalVariable = ""; 046 private String _stateFormula = ""; 047 private ExpressionNode _stateExpressionNode; 048 049 public ActionPositionableByClass(String sys, String user) 050 throws BadUserNameException, BadSystemNameException { 051 super(sys, user); 052 } 053 054 @Override 055 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 056 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 057 String sysName = systemNames.get(getSystemName()); 058 String userName = userNames.get(getSystemName()); 059 if (sysName == null) sysName = manager.getAutoSystemName(); 060 ActionPositionableByClass copy = new ActionPositionableByClass(sysName, userName); 061 copy.setComment(getComment()); 062 copy.setEditor(_editorName); 063 copy.setClassName(_className); 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 setEditor(@CheckForNull String editorName) { 077 assertListenersAreNotRegistered(log, "setEditor"); 078 _editorName = editorName; 079 if (editorName != null) { 080 _editor = jmri.InstanceManager.getDefault(jmri.jmrit.display.EditorManager.class).getByName(editorName); 081 } else { 082 _editor = null; 083 } 084// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 085 } 086 087 public String getEditorName() { 088 return _editorName; 089 } 090 091 public void setClassName(@CheckForNull String className) { 092 assertListenersAreNotRegistered(log, "setPositionable"); 093 _className = className; 094// InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 095 } 096 097 public String getClassName() { 098 return _className; 099 } 100 101 public void setAddressing(NamedBeanAddressing addressing) throws ParserException { 102 _addressing = addressing; 103 parseFormula(); 104 } 105 106 public NamedBeanAddressing getAddressing() { 107 return _addressing; 108 } 109 110 public void setReference(@Nonnull String reference) { 111 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 112 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 113 } 114 _reference = reference; 115 } 116 117 public String getReference() { 118 return _reference; 119 } 120 121 public void setLocalVariable(@Nonnull String localVariable) { 122 _localVariable = localVariable; 123 } 124 125 public String getLocalVariable() { 126 return _localVariable; 127 } 128 129 public void setFormula(@Nonnull String formula) throws ParserException { 130 _formula = formula; 131 parseFormula(); 132 } 133 134 public String getFormula() { 135 return _formula; 136 } 137 138 private void parseFormula() throws ParserException { 139 if (_addressing == NamedBeanAddressing.Formula) { 140 Map<String, Variable> variables = new HashMap<>(); 141 142 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 143 _expressionNode = parser.parseExpression(_formula); 144 } else { 145 _expressionNode = null; 146 } 147 } 148 149 public void setStateAddressing(NamedBeanAddressing addressing) throws ParserException { 150 _stateAddressing = addressing; 151 parseStateFormula(); 152 } 153 154 public NamedBeanAddressing getStateAddressing() { 155 return _stateAddressing; 156 } 157 158 public void setOperation(Operation isControlling) { 159 _operation = isControlling; 160 } 161 162 public Operation getOperation() { 163 return _operation; 164 } 165 166 public void setStateReference(@Nonnull String reference) { 167 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 168 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 169 } 170 _stateReference = reference; 171 } 172 173 public String getStateReference() { 174 return _stateReference; 175 } 176 177 public void setStateLocalVariable(@Nonnull String localVariable) { 178 _stateLocalVariable = localVariable; 179 } 180 181 public String getStateLocalVariable() { 182 return _stateLocalVariable; 183 } 184 185 public void setStateFormula(@Nonnull String formula) throws ParserException { 186 _stateFormula = formula; 187 parseStateFormula(); 188 } 189 190 public String getStateFormula() { 191 return _stateFormula; 192 } 193 194 private void parseStateFormula() throws ParserException { 195 if (_stateAddressing == NamedBeanAddressing.Formula) { 196 Map<String, Variable> variables = new HashMap<>(); 197 198 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 199 _stateExpressionNode = parser.parseExpression(_stateFormula); 200 } else { 201 _stateExpressionNode = null; 202 } 203 } 204 205 @Override 206 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 207/* 208 if ("CanDelete".equals(evt.getPropertyName())) { // No I18N 209 if (evt.getOldValue() instanceof Turnout) { 210 if (evt.getOldValue().equals(getTurnout().getBean())) { 211 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 212 throw new PropertyVetoException(Bundle.getMessage("Turnout_TurnoutInUseTurnoutExpressionVeto", getDisplayName()), e); // NOI18N 213 } 214 } 215 } else if ("DoDelete".equals(evt.getPropertyName())) { // No I18N 216 if (evt.getOldValue() instanceof Turnout) { 217 if (evt.getOldValue().equals(getTurnout().getBean())) { 218 removeTurnout(); 219 } 220 } 221 } 222*/ 223 } 224 225 /** {@inheritDoc} */ 226 @Override 227 public LogixNG_Category getCategory() { 228 return CategoryDisplay.DISPLAY; 229 } 230 231 private String getNewState() throws JmriException { 232 233 switch (_stateAddressing) { 234 case Reference: 235 return ReferenceUtil.getReference( 236 getConditionalNG().getSymbolTable(), _stateReference); 237 238 case LocalVariable: 239 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 240 return TypeConversionUtil 241 .convertToString(symbolTable.getValue(_stateLocalVariable), false); 242 243 case Formula: 244 return _stateExpressionNode != null 245 ? TypeConversionUtil.convertToString( 246 _stateExpressionNode.calculate( 247 getConditionalNG().getSymbolTable()), false) 248 : null; 249 250 default: 251 throw new IllegalArgumentException("invalid _addressing state: " + _stateAddressing.name()); 252 } 253 } 254 255 /** {@inheritDoc} */ 256 @Override 257 public void execute() throws JmriException { 258 String className; 259 260// System.out.format("ActionPositionableByClass.execute: %s%n", getLongDescription()); 261 262 switch (_addressing) { 263 case Direct: 264 className = _className; 265 break; 266 267 case Reference: 268 className = ReferenceUtil.getReference( 269 getConditionalNG().getSymbolTable(), _reference); 270 break; 271 272 case LocalVariable: 273 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 274 className = TypeConversionUtil.convertToString( 275 symbolTable.getValue(_localVariable), false); 276 break; 277 278 case Formula: 279 className = _expressionNode != null ? 280 TypeConversionUtil.convertToString(_expressionNode.calculate( 281 getConditionalNG().getSymbolTable()), false) 282 : null; 283 break; 284 285 default: 286 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 287 } 288 289// System.out.format("ActionPositionableByClass.execute: positionable: %s%n", positionable); 290 291 if (className == null) { 292 log.error("className is null"); 293 return; 294 } 295 296 Set<Positionable> positionableSet = _editor.getPositionablesByClassName(className); 297 298 if (positionableSet == null) { 299 var lng = getConditionalNG(); 300 var cng = getConditionalNG(); 301 var m = getModule(); 302 String errorMessage; 303 if (m != null) { 304 errorMessage = Bundle.getMessage( 305 "ActionPositionableByClass_ErrorNoPositionables_Module", 306 getLongDescription(), m.getDisplayName(), getSystemName()); 307 } else { 308 errorMessage = Bundle.getMessage( 309 "ActionPositionableByClass_ErrorNoPositionables_LogixNG", 310 getLongDescription(), lng.getDisplayName(), cng.getDisplayName(), getSystemName()); 311 } 312 List<String> list = Arrays.asList(errorMessage.split("\n")); 313 throw new JmriException(Bundle.getMessage("ActionPositionableByClass_ErrorNoPositionables"), list); 314 } 315 316 String name = (_stateAddressing != NamedBeanAddressing.Direct) 317 ? getNewState() : null; 318 319 Operation operation; 320 if ((_stateAddressing == NamedBeanAddressing.Direct)) { 321 operation = _operation; 322 } else { 323 operation = Operation.valueOf(name); 324 } 325 326 ThreadingUtil.runOnGUI(() -> { 327 for (Positionable p : positionableSet) { 328 switch (operation) { 329 case Disable: 330 p.setControlling(false); 331 break; 332 case Enable: 333 p.setControlling(true); 334 break; 335 case Hide: 336 p.setHidden(true); 337 break; 338 case Show: 339 p.setHidden(false); 340 break; 341 default: 342 throw new RuntimeException("operation has invalid value: "+operation.name()); 343 } 344 } 345 if (_editor instanceof LayoutEditor) { 346 // Insure that the panel has been updated 347 ((LayoutEditor) _editor).redrawPanel(); 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, "ActionPositionableByClass_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 className; 376 if (this._className != null) { 377 className = this._className; 378 } else { 379 className = Bundle.getMessage(locale, "BeanNotSelected"); 380 } 381 positonableName = Bundle.getMessage(locale, "AddressByDirect", className); 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, "ActionPositionableByClass_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 } 431 432 /** {@inheritDoc} */ 433 @Override 434 public void registerListenersForThisClass() { 435 } 436 437 /** {@inheritDoc} */ 438 @Override 439 public void unregisterListenersForThisClass() { 440 } 441 442 /** {@inheritDoc} */ 443 @Override 444 public void disposeMe() { 445 } 446 447 448 public enum Operation { 449 Disable(Bundle.getMessage("ActionPositionableByClass_Disable")), 450 Enable(Bundle.getMessage("ActionPositionableByClass_Enable")), 451 Hide(Bundle.getMessage("ActionPositionableByClass_Hide")), 452 Show(Bundle.getMessage("ActionPositionableByClass_Show")); 453 454 private final String _text; 455 456 private Operation(String text) { 457 this._text = text; 458 } 459 460 @Override 461 public String toString() { 462 return _text; 463 } 464 465 } 466 467 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionPositionableByClass.class); 468 469}