001package jmri.jmrit.logixng.actions; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.*; 006 007import javax.annotation.Nonnull; 008 009import jmri.InstanceManager; 010import jmri.JmriException; 011import jmri.jmrit.Sound; 012import jmri.jmrit.logixng.*; 013import jmri.jmrit.logixng.util.LogixNG_SelectEnum; 014import jmri.jmrit.logixng.util.ReferenceUtil; 015import jmri.jmrit.logixng.util.parser.*; 016import jmri.util.ThreadingUtil; 017import jmri.util.TypeConversionUtil; 018 019/** 020 * Plays a sound. 021 * 022 * @author Daniel Bergqvist Copyright 2021 023 */ 024public class ActionSound extends AbstractDigitalAction 025 implements PropertyChangeListener { 026 027 private final LogixNG_SelectEnum<Operation> _selectEnum = 028 new LogixNG_SelectEnum<>(this, Operation.values(), Operation.Play, this); 029 030 private NamedBeanAddressing _soundAddressing = NamedBeanAddressing.Direct; 031 private String _sound = ""; 032 private String _soundReference = ""; 033 private String _soundLocalVariable = ""; 034 private String _soundFormula = ""; 035 private ExpressionNode _soundExpressionNode; 036 037 public ActionSound(String sys, String user) 038 throws BadUserNameException, BadSystemNameException { 039 super(sys, user); 040 } 041 042 @Override 043 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 044 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 045 String sysName = systemNames.get(getSystemName()); 046 String userName = userNames.get(getSystemName()); 047 if (sysName == null) sysName = manager.getAutoSystemName(); 048 ActionSound copy = new ActionSound(sysName, userName); 049 copy.setComment(getComment()); 050 copy.setSound(_sound); 051 _selectEnum.copy(copy._selectEnum); 052 copy.setSoundAddressing(_soundAddressing); 053 copy.setSoundFormula(_soundFormula); 054 copy.setSoundLocalVariable(_soundLocalVariable); 055 copy.setSoundReference(_soundReference); 056 return manager.registerAction(copy); 057 } 058 059 public LogixNG_SelectEnum<Operation> getSelectEnum() { 060 return _selectEnum; 061 } 062 063 public void setSoundAddressing(NamedBeanAddressing addressing) throws ParserException { 064 _soundAddressing = addressing; 065 parseSoundFormula(); 066 } 067 068 public NamedBeanAddressing getSoundAddressing() { 069 return _soundAddressing; 070 } 071 072 public void setSound(String sound) { 073 if (sound == null) _sound = ""; 074 else _sound = sound; 075 } 076 077 public String getSound() { 078 return _sound; 079 } 080 081 public void setSoundReference(@Nonnull String reference) { 082 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 083 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 084 } 085 _soundReference = reference; 086 } 087 088 public String getSoundReference() { 089 return _soundReference; 090 } 091 092 public void setSoundLocalVariable(@Nonnull String localVariable) { 093 _soundLocalVariable = localVariable; 094 } 095 096 public String getSoundLocalVariable() { 097 return _soundLocalVariable; 098 } 099 100 public void setSoundFormula(@Nonnull String formula) throws ParserException { 101 _soundFormula = formula; 102 parseSoundFormula(); 103 } 104 105 public String getSoundFormula() { 106 return _soundFormula; 107 } 108 109 private void parseSoundFormula() throws ParserException { 110 if (_soundAddressing == NamedBeanAddressing.Formula) { 111 Map<String, Variable> variables = new HashMap<>(); 112 113 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 114 _soundExpressionNode = parser.parseExpression(_soundFormula); 115 } else { 116 _soundExpressionNode = null; 117 } 118 } 119 120 /** {@inheritDoc} */ 121 @Override 122 public Category getCategory() { 123 return Category.ITEM; 124 } 125 126 private String getTheSound() throws JmriException { 127 128 switch (_soundAddressing) { 129 case Direct: 130 return _sound; 131 132 case Reference: 133 return ReferenceUtil.getReference(getConditionalNG().getSymbolTable(), _soundReference); 134 135 case LocalVariable: 136 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 137 return TypeConversionUtil 138 .convertToString(symbolTable.getValue(_soundLocalVariable), false); 139 140 case Formula: 141 return _soundExpressionNode != null 142 ? TypeConversionUtil.convertToString( 143 _soundExpressionNode.calculate( 144 getConditionalNG().getSymbolTable()), false) 145 : ""; 146 147 default: 148 throw new IllegalArgumentException("invalid _soundAddressing state: " + _soundAddressing.name()); 149 } 150 } 151 152 /** {@inheritDoc} */ 153 @Override 154 public void execute() throws JmriException { 155 156 Operation operation = _selectEnum.evaluateEnum(getConditionalNG()); 157 String path = getTheSound(); 158 159 ThreadingUtil.runOnLayoutWithJmriException(() -> { 160 switch (operation) { 161 case Play: 162 if (!path.equals("")) { 163 try { 164 new Sound(path).play(true); 165 } catch (NullPointerException ex) { 166 throw new JmriException(Bundle.getMessage("ActionSound_Error_SoundNotFound", path)); 167 } 168 } 169 break; 170 171 default: 172 throw new IllegalArgumentException("invalid operation: " + operation.name()); 173 } 174 }); 175 } 176 177 @Override 178 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 179 throw new UnsupportedOperationException("Not supported."); 180 } 181 182 @Override 183 public int getChildCount() { 184 return 0; 185 } 186 187 @Override 188 public String getShortDescription(Locale locale) { 189 return Bundle.getMessage(locale, "ActionSound_Short"); 190 } 191 192 @Override 193 public String getLongDescription(Locale locale) { 194 String operation = _selectEnum.getDescription(locale); 195 String sound; 196 197 switch (_soundAddressing) { 198 case Direct: 199 sound = Bundle.getMessage(locale, "AddressByDirect", _sound); 200 break; 201 202 case Reference: 203 sound = Bundle.getMessage(locale, "AddressByReference", _soundReference); 204 break; 205 206 case LocalVariable: 207 sound = Bundle.getMessage(locale, "AddressByLocalVariable", _soundLocalVariable); 208 break; 209 210 case Formula: 211 sound = Bundle.getMessage(locale, "AddressByFormula", _soundFormula); 212 break; 213 214 default: 215 throw new IllegalArgumentException("invalid _stateAddressing state: " + _soundAddressing.name()); 216 } 217 218 if (_selectEnum.getAddressing() == NamedBeanAddressing.Direct) { 219 if (_selectEnum.getEnum() == Operation.Play) { 220 return Bundle.getMessage(locale, "ActionSound_Long_Play", sound); 221 } else { 222 return Bundle.getMessage(locale, "ActionSound_Long", operation, sound); 223 } 224 } else { 225 return Bundle.getMessage(locale, "ActionSound_LongUnknownOper", operation, sound); 226 } 227 } 228 229 /** {@inheritDoc} */ 230 @Override 231 public void setup() { 232 // Do nothing 233 } 234 235 /** {@inheritDoc} */ 236 @Override 237 public void registerListenersForThisClass() { 238 if (!_listenersAreRegistered) { 239 _listenersAreRegistered = true; 240 } 241 _selectEnum.registerListeners(); 242 } 243 244 /** {@inheritDoc} */ 245 @Override 246 public void unregisterListenersForThisClass() { 247 if (_listenersAreRegistered) { 248 _listenersAreRegistered = false; 249 } 250 _selectEnum.unregisterListeners(); 251 } 252 253 /** {@inheritDoc} */ 254 @Override 255 public void firePropertyChange(String p, Object old, Object n) { 256 super.firePropertyChange(p, old, n); 257 } 258 259 /** {@inheritDoc} */ 260 @Override 261 public void disposeMe() { 262 // Do nothing 263 } 264 265 266 public enum Operation { 267 Play(Bundle.getMessage("ActionSound_Operation_Play")); 268 269 private final String _text; 270 271 private Operation(String text) { 272 this._text = text; 273 } 274 275 @Override 276 public String toString() { 277 return _text; 278 } 279 280 } 281 282 /** {@inheritDoc} */ 283 @Override 284 public void propertyChange(PropertyChangeEvent evt) { 285 getConditionalNG().execute(); 286 } 287 288// private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionSound.class); 289 290}