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