001package jmri.jmrit.logixng.expressions; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.*; 006 007import javax.annotation.Nonnull; 008 009import jmri.*; 010import jmri.Conditional; 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.util.TypeConversionUtil; 016 017/** 018 * Evaluates the state of a Conditional. 019 * 020 * @author Daniel Bergqvist Copyright 2018 021 */ 022public class ExpressionConditional extends AbstractDigitalExpression 023 implements PropertyChangeListener { 024 025 private final LogixNG_SelectNamedBean<Conditional> _selectNamedBean = 026 new LogixNG_SelectNamedBean<>( 027 this, Conditional.class, InstanceManager.getDefault(ConditionalManager.class), this); 028 private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is; 029 private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct; 030 private ConditionalState _conditionalState = ConditionalState.False; 031 private String _stateReference = ""; 032 private String _stateLocalVariable = ""; 033 private String _stateFormula = ""; 034 private ExpressionNode _stateExpressionNode; 035 036 public ExpressionConditional(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 JmriException { 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 ExpressionConditional copy = new ExpressionConditional(sysName, userName); 048 copy.setComment(getComment()); 049 copy.set_Is_IsNot(_is_IsNot); 050 _selectNamedBean.copy(copy._selectNamedBean); 051 copy.setConditionalState(_conditionalState); 052 copy.setStateAddressing(_stateAddressing); 053 copy.setStateReference(_stateReference); 054 copy.setStateLocalVariable(_stateLocalVariable); 055 copy.setStateFormula(_stateFormula); 056 return manager.registerExpression(copy).deepCopyChildren(this, systemNames, userNames); 057 } 058 059 public LogixNG_SelectNamedBean<Conditional> 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 setConditionalState(ConditionalState state) { 081 _conditionalState = state; 082 } 083 084 public ConditionalState getConditionalState() { 085 return _conditionalState; 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 Conditional conditional = _selectNamedBean.evaluateNamedBean(getConditionalNG()); 161 162 if (conditional == null) return false; 163 164 ConditionalState checkConditionalState; 165 166 if ((_stateAddressing == NamedBeanAddressing.Direct)) { 167 checkConditionalState = _conditionalState; 168 } else { 169 checkConditionalState = ConditionalState.valueOf(getNewState()); 170 } 171 172 if (_is_IsNot == Is_IsNot_Enum.Is) { 173 return conditional.getState() == checkConditionalState.getID(); 174 } else { 175 return conditional.getState() != checkConditionalState.getID(); 176 } 177 } 178 179 @Override 180 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 181 throw new UnsupportedOperationException("Not supported."); 182 } 183 184 @Override 185 public int getChildCount() { 186 return 0; 187 } 188 189 @Override 190 public String getShortDescription(Locale locale) { 191 return Bundle.getMessage(locale, "Conditional_Short"); 192 } 193 194 @Override 195 public String getLongDescription(Locale locale) { 196 String namedBean = _selectNamedBean.getDescription(locale); 197 String state; 198 199 switch (_stateAddressing) { 200 case Direct: 201 state = Bundle.getMessage(locale, "AddressByDirect", _conditionalState.toString()); 202 break; 203 204 case Reference: 205 state = Bundle.getMessage(locale, "AddressByReference", _stateReference); 206 break; 207 208 case LocalVariable: 209 state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable); 210 break; 211 212 case Formula: 213 state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula); 214 break; 215 216 default: 217 throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name()); 218 } 219 220 return Bundle.getMessage(locale, "Conditional_Long", namedBean, _is_IsNot.toString(), state); 221 } 222 223 /** {@inheritDoc} */ 224 @Override 225 public void setup() { 226 // Do nothing 227 } 228 229 /** {@inheritDoc} */ 230 @Override 231 public void registerListenersForThisClass() { 232 if (!_listenersAreRegistered) { 233 _selectNamedBean.addPropertyChangeListener("KnownState", this); 234 _selectNamedBean.registerListeners(); 235 _listenersAreRegistered = true; 236 } 237 } 238 239 /** {@inheritDoc} */ 240 @Override 241 public void unregisterListenersForThisClass() { 242 if (_listenersAreRegistered) { 243 _selectNamedBean.removePropertyChangeListener("KnownState", this); 244 _selectNamedBean.unregisterListeners(); 245 _listenersAreRegistered = false; 246 } 247 } 248 249 /** {@inheritDoc} */ 250 @Override 251 public void propertyChange(PropertyChangeEvent evt) { 252 getConditionalNG().execute(); 253 } 254 255 /** {@inheritDoc} */ 256 @Override 257 public void disposeMe() { 258 } 259 260 261 262 public enum ConditionalState { 263 False(Conditional.FALSE, Bundle.getMessage("ConditionalStateFalse")), 264 True(Conditional.TRUE, Bundle.getMessage("ConditionalStateTrue")), 265 Other(-1, Bundle.getMessage("ConditionalOtherStatus")); 266 267 private final int _id; 268 private final String _text; 269 270 private ConditionalState(int id, String text) { 271 this._id = id; 272 this._text = text; 273 } 274 275 static public ConditionalState get(int id) { 276 switch (id) { 277 case Conditional.FALSE: 278 return False; 279 280 case Conditional.TRUE: 281 return True; 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 /** {@inheritDoc} */ 300 @Override 301 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 302 log.debug("getUsageReport :: ExpressionConditional: bean = {}, report = {}", cdl, report); 303 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression); 304 } 305 306 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionConditional.class); 307 308}