001package jmri.jmrit.logixng.expressions; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.beans.VetoableChangeListener; 006import java.util.*; 007 008import javax.annotation.Nonnull; 009 010import jmri.*; 011import jmri.jmrit.logixng.*; 012import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 013import jmri.jmrit.logixng.util.ReferenceUtil; 014import jmri.jmrit.logixng.util.parser.*; 015import jmri.jmrit.logixng.util.parser.ExpressionNode; 016import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 017import jmri.util.TypeConversionUtil; 018 019/** 020 * Evaluates the state of a SignalMast. 021 * 022 * @author Daniel Bergqvist Copyright 2020 023 */ 024public class ExpressionSignalMast extends AbstractDigitalExpression 025 implements PropertyChangeListener, VetoableChangeListener { 026 027 private final LogixNG_SelectNamedBean<SignalMast> _selectNamedBean = 028 new LogixNG_SelectNamedBean<>( 029 this, SignalMast.class, InstanceManager.getDefault(SignalMastManager.class), this); 030 031 private NamedBeanAddressing _queryAddressing = NamedBeanAddressing.Direct; 032 private QueryType _queryType = QueryType.Aspect; 033 private String _queryReference = ""; 034 private String _queryLocalVariable = ""; 035 private String _queryFormula = ""; 036 private ExpressionNode _queryExpressionNode; 037 038 private NamedBeanAddressing _aspectAddressing = NamedBeanAddressing.Direct; 039 private String _signalMastAspect = ""; 040 private String _aspectReference = ""; 041 private String _aspectLocalVariable = ""; 042 private String _aspectFormula = ""; 043 private ExpressionNode _aspectExpressionNode; 044 045 private final LogixNG_SelectNamedBean<SignalMast> _selectExampleNamedBean = 046 new LogixNG_SelectNamedBean<>( 047 this, SignalMast.class, InstanceManager.getDefault(SignalMastManager.class), this); 048 049 050 public ExpressionSignalMast(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 ExpressionSignalMast copy = new ExpressionSignalMast(sysName, userName); 062 copy.setComment(getComment()); 063 _selectNamedBean.copy(copy._selectNamedBean); 064 copy.setAspect(_signalMastAspect); 065 copy.setQueryAddressing(_queryAddressing); 066 copy.setQueryType(_queryType); 067 copy.setQueryFormula(_queryFormula); 068 copy.setQueryLocalVariable(_queryLocalVariable); 069 copy.setQueryReference(_queryReference); 070 copy.setAspectAddressing(_aspectAddressing); 071 copy.setAspectFormula(_aspectFormula); 072 copy.setAspectLocalVariable(_aspectLocalVariable); 073 copy.setAspectReference(_aspectReference); 074 _selectExampleNamedBean.copy(copy._selectExampleNamedBean); 075 return manager.registerExpression(copy).deepCopyChildren(this, systemNames, userNames); 076 } 077 078 public LogixNG_SelectNamedBean<SignalMast> getSelectNamedBean() { 079 return _selectNamedBean; 080 } 081 082 public LogixNG_SelectNamedBean<SignalMast> getSelectExampleNamedBean() { 083 return _selectExampleNamedBean; 084 } 085 086 public void setQueryAddressing(NamedBeanAddressing addressing) throws ParserException { 087 _queryAddressing = addressing; 088 parseQueryFormula(); 089 } 090 091 public NamedBeanAddressing getQueryAddressing() { 092 return _queryAddressing; 093 } 094 095 public void setQueryType(QueryType queryType) { 096 _queryType = queryType; 097 } 098 099 public QueryType getQueryType() { 100 return _queryType; 101 } 102 103 public void setQueryReference(@Nonnull String reference) { 104 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 105 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 106 } 107 _queryReference = reference; 108 } 109 110 public String getQueryReference() { 111 return _queryReference; 112 } 113 114 public void setQueryLocalVariable(@Nonnull String localVariable) { 115 _queryLocalVariable = localVariable; 116 } 117 118 public String getQueryLocalVariable() { 119 return _queryLocalVariable; 120 } 121 122 public void setQueryFormula(@Nonnull String formula) throws ParserException { 123 _queryFormula = formula; 124 parseQueryFormula(); 125 } 126 127 public String getQueryFormula() { 128 return _queryFormula; 129 } 130 131 private void parseQueryFormula() throws ParserException { 132 if (_queryAddressing == NamedBeanAddressing.Formula) { 133 Map<String, Variable> variables = new HashMap<>(); 134 135 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 136 _queryExpressionNode = parser.parseExpression(_queryFormula); 137 } else { 138 _queryExpressionNode = null; 139 } 140 } 141 142 public void setAspectAddressing(NamedBeanAddressing addressing) throws ParserException { 143 _aspectAddressing = addressing; 144 parseAspectFormula(); 145 } 146 147 public NamedBeanAddressing getAspectAddressing() { 148 return _aspectAddressing; 149 } 150 151 public void setAspect(String aspect) { 152 if (aspect == null) _signalMastAspect = ""; 153 else _signalMastAspect = aspect; 154 } 155 156 public String getAspect() { 157 return _signalMastAspect; 158 } 159 160 public void setAspectReference(@Nonnull String reference) { 161 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 162 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 163 } 164 _aspectReference = reference; 165 } 166 167 public String getAspectReference() { 168 return _aspectReference; 169 } 170 171 public void setAspectLocalVariable(@Nonnull String localVariable) { 172 _aspectLocalVariable = localVariable; 173 } 174 175 public String getAspectLocalVariable() { 176 return _aspectLocalVariable; 177 } 178 179 public void setAspectFormula(@Nonnull String formula) throws ParserException { 180 _aspectFormula = formula; 181 parseAspectFormula(); 182 } 183 184 public String getAspectFormula() { 185 return _aspectFormula; 186 } 187 188 private void parseAspectFormula() throws ParserException { 189 if (_aspectAddressing == NamedBeanAddressing.Formula) { 190 Map<String, Variable> variables = new HashMap<>(); 191 192 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 193 _aspectExpressionNode = parser.parseExpression(_aspectFormula); 194 } else { 195 _aspectExpressionNode = null; 196 } 197 } 198 199 /** {@inheritDoc} */ 200 @Override 201 public Category getCategory() { 202 return Category.ITEM; 203 } 204 205 private String getNewAspect(ConditionalNG conditionalNG) throws JmriException { 206 207 switch (_aspectAddressing) { 208 case Direct: 209 return _signalMastAspect; 210 211 case Reference: 212 return ReferenceUtil.getReference( 213 conditionalNG.getSymbolTable(), _aspectReference); 214 215 case LocalVariable: 216 SymbolTable symbolTable = conditionalNG.getSymbolTable(); 217 return TypeConversionUtil 218 .convertToString(symbolTable.getValue(_aspectLocalVariable), false); 219 220 case Formula: 221 return _aspectExpressionNode != null 222 ? TypeConversionUtil.convertToString( 223 _aspectExpressionNode.calculate( 224 conditionalNG.getSymbolTable()), false) 225 : ""; 226 227 default: 228 throw new IllegalArgumentException("invalid _aspectAddressing state: " + _aspectAddressing.name()); 229 } 230 } 231 232 private QueryType getQuery(ConditionalNG conditionalNG) throws JmriException { 233 234 String oper = ""; 235 try { 236 switch (_queryAddressing) { 237 case Direct: 238 return _queryType; 239 240 case Reference: 241 oper = ReferenceUtil.getReference( 242 conditionalNG.getSymbolTable(), _queryReference); 243 return QueryType.valueOf(oper); 244 245 case LocalVariable: 246 SymbolTable symbolTable = 247 conditionalNG.getSymbolTable(); 248 oper = TypeConversionUtil 249 .convertToString(symbolTable.getValue(_queryLocalVariable), false); 250 return QueryType.valueOf(oper); 251 252 case Formula: 253 if (_aspectExpressionNode != null) { 254 oper = TypeConversionUtil.convertToString( 255 _queryExpressionNode.calculate( 256 conditionalNG.getSymbolTable()), false); 257 return QueryType.valueOf(oper); 258 } else { 259 return null; 260 } 261 default: 262 throw new IllegalArgumentException("invalid _addressing state: " + _queryAddressing.name()); 263 } 264 } catch (IllegalArgumentException e) { 265 throw new JmriException("Unknown query: "+oper, e); 266 } 267 } 268 269 /** {@inheritDoc} */ 270 @Override 271 public boolean evaluate() throws JmriException { 272 final ConditionalNG conditionalNG = getConditionalNG(); 273 274 SignalMast signalMast = _selectNamedBean.evaluateNamedBean(conditionalNG); 275 276 if (signalMast == null) return false; 277 278 QueryType query = getQuery(conditionalNG); 279 280 boolean result = false; 281 282 switch (query) { 283 case Aspect: 284 if (signalMast.getAspect() != null) { 285 result = getNewAspect(conditionalNG).equals(signalMast.getAspect()); 286 } 287 break; 288 case NotAspect: 289 if (signalMast.getAspect() != null) { 290 result = ! getNewAspect(conditionalNG).equals(signalMast.getAspect()); 291 } 292 break; 293 case Lit: 294 result = signalMast.getLit(); 295 break; 296 case NotLit: 297 result = ! signalMast.getLit(); 298 break; 299 case Held: 300 result = signalMast.getHeld(); 301 break; 302 case NotHeld: 303 result = ! signalMast.getHeld(); 304 break; 305 case IsPermissiveSmlDisabled: 306 result = signalMast.isPermissiveSmlDisabled(); 307 break; 308 case IsPermissiveSmlNotDisabled: 309 result = ! signalMast.isPermissiveSmlDisabled(); 310 break; 311 default: 312 throw new RuntimeException("Unknown enum: "+_queryType.name()); 313 } 314 315 return result; 316 } 317 318 @Override 319 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 320 throw new UnsupportedOperationException("Not supported."); 321 } 322 323 @Override 324 public int getChildCount() { 325 return 0; 326 } 327 328 @Override 329 public String getShortDescription(Locale locale) { 330 return Bundle.getMessage(locale, "SignalMast_Short"); 331 } 332 333 @Override 334 public String getLongDescription(Locale locale) { 335 String namedBean = _selectNamedBean.getDescription(locale); 336 String query; 337 String aspect; 338 339 switch (_queryAddressing) { 340 case Direct: 341 query = Bundle.getMessage(locale, "AddressByDirect", _queryType._text); 342 break; 343 344 case Reference: 345 query = Bundle.getMessage(locale, "AddressByReference", _queryReference); 346 break; 347 348 case LocalVariable: 349 query = Bundle.getMessage(locale, "AddressByLocalVariable", _queryLocalVariable); 350 break; 351 352 case Formula: 353 query = Bundle.getMessage(locale, "AddressByFormula", _queryFormula); 354 break; 355 356 default: 357 throw new IllegalArgumentException("invalid _queryAddressing state: " + _queryAddressing.name()); 358 } 359 360 switch (_aspectAddressing) { 361 case Direct: 362 aspect = Bundle.getMessage(locale, "AddressByDirect", _signalMastAspect); 363 break; 364 365 case Reference: 366 aspect = Bundle.getMessage(locale, "AddressByReference", _aspectReference); 367 break; 368 369 case LocalVariable: 370 aspect = Bundle.getMessage(locale, "AddressByLocalVariable", _aspectLocalVariable); 371 break; 372 373 case Formula: 374 aspect = Bundle.getMessage(locale, "AddressByFormula", _aspectFormula); 375 break; 376 377 default: 378 throw new IllegalArgumentException("invalid _stateAddressing state: " + _aspectAddressing.name()); 379 } 380 381 if (_queryAddressing == NamedBeanAddressing.Direct) { 382 if (_queryType == QueryType.Aspect) { 383 return Bundle.getMessage(locale, "SignalMast_LongAspect", namedBean, aspect); 384 } if (_queryType == QueryType.NotAspect) { 385 return Bundle.getMessage(locale, "SignalMast_LongNotAspect", namedBean, aspect); 386 } else { 387 return Bundle.getMessage(locale, "SignalMast_Long", namedBean, query); 388 } 389 } else { 390 return Bundle.getMessage(locale, "SignalMast_LongUnknownOper", namedBean, query, aspect); 391 } 392 } 393 394 /** {@inheritDoc} */ 395 @Override 396 public void setup() { 397 // Do nothing 398 } 399 400 /** {@inheritDoc} */ 401 @Override 402 public void registerListenersForThisClass() { 403 SignalMast signalMast = _selectNamedBean.getNamedBeanIfDirectAddressing(); 404 405 if (!_listenersAreRegistered && (signalMast != null)) { 406 switch (_queryType) { 407 case Aspect: 408 case NotAspect: 409 signalMast.addPropertyChangeListener("Aspect", this); 410 break; 411 412 case Lit: 413 case NotLit: 414 signalMast.addPropertyChangeListener("Lit", this); 415 break; 416 417 case Held: 418 case NotHeld: 419 signalMast.addPropertyChangeListener("Held", this); 420 break; 421 422 case IsPermissiveSmlDisabled: 423 case IsPermissiveSmlNotDisabled: 424 signalMast.addPropertyChangeListener("PermissiveSmlDisabled", this); 425 break; 426 427 default: 428 throw new RuntimeException("Unknown enum: "+_queryType.name()); 429 } 430 _selectNamedBean.registerListeners(); 431 _listenersAreRegistered = true; 432 } 433 } 434 435 /** {@inheritDoc} */ 436 @Override 437 public void unregisterListenersForThisClass() { 438 SignalMast signalMast = _selectNamedBean.getNamedBeanIfDirectAddressing(); 439 440 if (_listenersAreRegistered && (signalMast != null)) { 441 switch (_queryType) { 442 case Aspect: 443 case NotAspect: 444 signalMast.removePropertyChangeListener("Aspect", this); 445 break; 446 447 case Lit: 448 case NotLit: 449 signalMast.removePropertyChangeListener("Lit", this); 450 break; 451 452 case Held: 453 case NotHeld: 454 signalMast.removePropertyChangeListener("Held", this); 455 break; 456 457 case IsPermissiveSmlDisabled: 458 case IsPermissiveSmlNotDisabled: 459 signalMast.removePropertyChangeListener("PermissiveSmlDisabled", this); 460 break; 461 462 default: 463 throw new RuntimeException("Unknown enum: "+_queryType.name()); 464 } 465 _selectNamedBean.unregisterListeners(); 466 _listenersAreRegistered = false; 467 } 468 } 469 470 /** {@inheritDoc} */ 471 @Override 472 public void propertyChange(PropertyChangeEvent evt) { 473 getConditionalNG().execute(); 474 } 475 476 /** {@inheritDoc} */ 477 @Override 478 public void disposeMe() { 479 } 480 481 482 483 public enum QueryType { 484 Aspect(Bundle.getMessage("SignalMastQueryType_Aspect")), 485 NotAspect(Bundle.getMessage("SignalMastQueryType_NotAspect")), 486 Lit(Bundle.getMessage("SignalMastQueryType_Lit")), 487 NotLit(Bundle.getMessage("SignalMastQueryType_NotLit")), 488 Held(Bundle.getMessage("SignalMastQueryType_Held")), 489 NotHeld(Bundle.getMessage("SignalMastQueryType_NotHeld")), 490 IsPermissiveSmlDisabled(Bundle.getMessage("SignalMastQueryType_IsPermissiveSmlDisabled")), 491 IsPermissiveSmlNotDisabled(Bundle.getMessage("SignalMastQueryType_IsPermissiveSmlNotDisabled")); 492 493 private final String _text; 494 495 private QueryType(String text) { 496 this._text = text; 497 } 498 499 @Override 500 public String toString() { 501 return _text; 502 } 503 504 } 505 506 /** {@inheritDoc} */ 507 @Override 508 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 509 log.debug("getUsageReport :: ExpressionSignalMast: bean = {}, report = {}", cdl, report); 510 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 511 _selectExampleNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 512 } 513 514 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionSignalMast.class); 515 516}