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