001package jmri.jmrit.logixng.expressions; 002 003import java.beans.*; 004import java.util.*; 005 006import javax.annotation.Nonnull; 007 008import jmri.*; 009import jmri.Audio; 010import jmri.AudioManager; 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 * This expression evaluates the state of an Audio. 021 * 022 * @author Daniel Bergqvist Copyright 2023 023 */ 024public class ExpressionAudio extends AbstractDigitalExpression 025 implements PropertyChangeListener { 026 027 private final LogixNG_SelectNamedBean<Audio> _selectNamedBean = 028 new LogixNG_SelectNamedBean<>( 029 this, Audio.class, InstanceManager.getDefault(AudioManager.class), this); 030 031 private boolean _hasChangedState = false; 032 033 private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is; 034 035 private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct; 036 private AudioState _audioState = AudioState.Initial; 037 private String _stateReference = ""; 038 private String _stateLocalVariable = ""; 039 private String _stateFormula = ""; 040 private ExpressionNode _stateExpressionNode; 041 042 private boolean _checkOnlyOnChange; 043 044 045 public ExpressionAudio(String sys, String user) 046 throws BadUserNameException, BadSystemNameException { 047 super(sys, user); 048 } 049 050 @Override 051 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 052 DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class); 053 String sysName = systemNames.get(getSystemName()); 054 String userName = userNames.get(getSystemName()); 055 if (sysName == null) sysName = manager.getAutoSystemName(); 056 ExpressionAudio copy = new ExpressionAudio(sysName, userName); 057 copy.setComment(getComment()); 058 059 _selectNamedBean.copy(copy._selectNamedBean); 060 061 copy.set_Is_IsNot(_is_IsNot); 062 063 copy.setStateAddressing(_stateAddressing); 064 copy.setBeanState(_audioState); 065 copy.setStateReference(_stateReference); 066 copy.setStateLocalVariable(_stateLocalVariable); 067 copy.setStateFormula(_stateFormula); 068 069 copy.setCheckOnlyOnChange(_checkOnlyOnChange); 070 071 return manager.registerExpression(copy); 072 } 073 074 public LogixNG_SelectNamedBean<Audio> getSelectNamedBean() { 075 return _selectNamedBean; 076 } 077 078 public void set_Is_IsNot(Is_IsNot_Enum is_IsNot) { 079 _is_IsNot = is_IsNot; 080 } 081 082 public Is_IsNot_Enum get_Is_IsNot() { 083 return _is_IsNot; 084 } 085 086 087 public void setStateAddressing(NamedBeanAddressing addressing) throws ParserException { 088 _stateAddressing = addressing; 089 parseStateFormula(); 090 } 091 092 public NamedBeanAddressing getStateAddressing() { 093 return _stateAddressing; 094 } 095 096 public void setBeanState(AudioState state) { 097 _audioState = state; 098 } 099 100 public AudioState getBeanState() { 101 return _audioState; 102 } 103 104 public void setStateReference(@Nonnull String reference) { 105 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 106 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 107 } 108 _stateReference = reference; 109 } 110 111 public String getStateReference() { 112 return _stateReference; 113 } 114 115 public void setStateLocalVariable(@Nonnull String localVariable) { 116 _stateLocalVariable = localVariable; 117 } 118 119 public String getStateLocalVariable() { 120 return _stateLocalVariable; 121 } 122 123 public void setStateFormula(@Nonnull String formula) throws ParserException { 124 _stateFormula = formula; 125 parseStateFormula(); 126 } 127 128 public String getStateFormula() { 129 return _stateFormula; 130 } 131 132 private void parseStateFormula() throws ParserException { 133 if (_stateAddressing == NamedBeanAddressing.Formula) { 134 Map<String, Variable> variables = new HashMap<>(); 135 136 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 137 _stateExpressionNode = parser.parseExpression(_stateFormula); 138 } else { 139 _stateExpressionNode = null; 140 } 141 } 142 143 144 public void setCheckOnlyOnChange(boolean triggerOnlyOnChange) { 145 _checkOnlyOnChange = triggerOnlyOnChange; 146 } 147 148 public boolean isCheckOnlyOnChange() { 149 return _checkOnlyOnChange; 150 } 151 152 153 /** {@inheritDoc} */ 154 @Override 155 public Category getCategory() { 156 return Category.ITEM; 157 } 158 159 private String getNewState() throws JmriException { 160 161 switch (_stateAddressing) { 162 case Reference: 163 return ReferenceUtil.getReference( 164 getConditionalNG().getSymbolTable(), _stateReference); 165 166 case LocalVariable: 167 SymbolTable symbolTable = 168 getConditionalNG().getSymbolTable(); 169 return TypeConversionUtil 170 .convertToString(symbolTable.getValue(_stateLocalVariable), false); 171 172 case Formula: 173 return _stateExpressionNode != null 174 ? TypeConversionUtil.convertToString( 175 _stateExpressionNode.calculate( 176 getConditionalNG().getSymbolTable()), false) 177 : null; 178 179 default: 180 throw new IllegalArgumentException("invalid _addressing state: " + _stateAddressing.name()); 181 } 182 } 183 184 /** {@inheritDoc} */ 185 @Override 186 public boolean evaluate() throws JmriException { 187 Audio audio = _selectNamedBean.evaluateNamedBean(getConditionalNG()); 188 189 if (audio == null) return false; 190 191 AudioState checkAudioState; 192 193 if ((_stateAddressing == NamedBeanAddressing.Direct)) { 194 checkAudioState = _audioState; 195 } else { 196 checkAudioState = AudioState.valueOf(getNewState()); 197 } 198 199 int currentState = audio.getState(); 200 201 if (_checkOnlyOnChange && !_hasChangedState) { 202 return false; 203 } 204 205 _hasChangedState = false; 206 207 if (_is_IsNot == Is_IsNot_Enum.Is) { 208 return currentState == checkAudioState.getID(); 209 } else { 210 return currentState != checkAudioState.getID(); 211 } 212 } 213 214 @Override 215 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 216 throw new UnsupportedOperationException("Not supported."); 217 } 218 219 @Override 220 public int getChildCount() { 221 return 0; 222 } 223 224 @Override 225 public String getShortDescription(Locale locale) { 226 return Bundle.getMessage(locale, "Audio_Short"); 227 } 228 229 @Override 230 public String getLongDescription(Locale locale) { 231 String namedBean = _selectNamedBean.getDescription(locale); 232 String state; 233 234 switch (_stateAddressing) { 235 case Direct: 236 state = Bundle.getMessage(locale, "AddressByDirect", _audioState._text); 237 break; 238 239 case Reference: 240 state = Bundle.getMessage(locale, "AddressByReference", _stateReference); 241 break; 242 243 case LocalVariable: 244 state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable); 245 break; 246 247 case Formula: 248 state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula); 249 break; 250 251 default: 252 throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name()); 253 } 254 255 256 if (_checkOnlyOnChange) { 257 return Bundle.getMessage(locale, "Audio_Long4", namedBean, _is_IsNot.toString(), state, Bundle.getMessage(locale, "Audio_CheckOnlyOnChange")); 258 } else { 259 return Bundle.getMessage(locale, "Audio_Long3", namedBean, _is_IsNot.toString(), state); 260 } 261 } 262 263 /** {@inheritDoc} */ 264 @Override 265 public void setup() { 266 // Do nothing 267 } 268 269 /** {@inheritDoc} */ 270 @Override 271 public void registerListenersForThisClass() { 272 if (!_listenersAreRegistered) { 273 _selectNamedBean.addPropertyChangeListener(this); 274 _selectNamedBean.registerListeners(); 275 _listenersAreRegistered = true; 276 } 277 } 278 279 /** {@inheritDoc} */ 280 @Override 281 public void unregisterListenersForThisClass() { 282 if (_listenersAreRegistered) { 283 _selectNamedBean.removePropertyChangeListener(this); 284 _selectNamedBean.unregisterListeners(); 285 _listenersAreRegistered = false; 286 } 287 } 288 289 /** {@inheritDoc} */ 290 @Override 291 public void propertyChange(PropertyChangeEvent evt) { 292 if (!Objects.equals(evt.getNewValue(), evt.getOldValue())) { 293 _hasChangedState = true; 294 } 295 getConditionalNG().execute(); 296 } 297 298 /** {@inheritDoc} */ 299 @Override 300 public void disposeMe() { 301 } 302 303 public enum AudioState { 304 Initial(Audio.STATE_INITIAL, Bundle.getMessage("Audio_StateInitial")), 305 Stopped(Audio.STATE_STOPPED, Bundle.getMessage("Audio_StateStopped")), 306 Playing(Audio.STATE_PLAYING, Bundle.getMessage("Audio_StatePlaying")), 307 Empty(Audio.STATE_EMPTY, Bundle.getMessage("Audio_StateEmpty")), 308 Loaded(Audio.STATE_LOADED, Bundle.getMessage("Audio_StateLoaded")), 309 Positioned(Audio.STATE_POSITIONED, Bundle.getMessage("Audio_StatePositioned")), 310 Moving(Audio.STATE_MOVING, Bundle.getMessage("Audio_StateMoving")); 311 312 private final int _id; 313 private final String _text; 314 315 private AudioState(int id, String text) { 316 this._id = id; 317 this._text = text; 318 } 319 320 public int getID() { 321 return _id; 322 } 323 324 @Override 325 public String toString() { 326 return _text; 327 } 328 } 329 330 /** {@inheritDoc} */ 331 @Override 332 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 333 log.debug("getUsageReport :: ExpressionAudio: bean = {}, report = {}", cdl, report); 334 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression); 335 } 336 337 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionAudio.class); 338 339}