001package jmri.jmrit.logixng.util; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyVetoException; 005import java.beans.VetoableChangeListener; 006import java.util.HashMap; 007import java.util.Locale; 008import java.util.Map; 009 010import javax.annotation.Nonnull; 011 012import jmri.*; 013import jmri.jmrit.logixng.*; 014import jmri.jmrit.logixng.implementation.AbstractBase; 015import jmri.jmrit.logixng.util.parser.*; 016import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 017import jmri.util.TypeConversionUtil; 018 019/** 020 * Select a boolean for LogixNG actions and expressions. 021 * 022 * @author Daniel Bergqvist (C) 2023 023 */ 024public class LogixNG_SelectBoolean implements VetoableChangeListener { 025 026 private final AbstractBase _base; 027 private final InUse _inUse; 028 private final LogixNG_SelectTable _selectTable; 029 030 private NamedBeanAddressing _addressing = NamedBeanAddressing.Direct; 031 private boolean _value; 032 private String _reference = ""; 033 private NamedBeanHandle<Memory> _memoryHandle; 034 private String _localVariable = ""; 035 private String _formula = ""; 036 private ExpressionNode _expressionNode; 037 038 039 public LogixNG_SelectBoolean(@Nonnull AbstractBase base) { 040 _base = base; 041 _inUse = () -> true; 042 _selectTable = new LogixNG_SelectTable(_base, _inUse); 043 } 044 045 public void copy(LogixNG_SelectBoolean copy) throws ParserException { 046 copy.setAddressing(_addressing); 047 copy.setValue(_value); 048 copy.setLocalVariable(_localVariable); 049 copy.setReference(_reference); 050 copy.setMemory(_memoryHandle); 051 copy.setFormula(_formula); 052 _selectTable.copy(copy._selectTable); 053 } 054 055 public void setAddressing(@Nonnull NamedBeanAddressing addressing) throws ParserException { 056 this._addressing = addressing; 057 parseFormula(); 058 } 059 060 public boolean isDirectAddressing() { 061 return _addressing == NamedBeanAddressing.Direct; 062 } 063 064 public NamedBeanAddressing getAddressing() { 065 return _addressing; 066 } 067 068 public void setValue(boolean value) { 069 _base.assertListenersAreNotRegistered(log, "setEnum"); 070 _value = value; 071 } 072 073 public boolean getValue() { 074 return _value; 075 } 076 077 public void setReference(@Nonnull String reference) { 078 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 079 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 080 } 081 _reference = reference; 082 } 083 084 public String getReference() { 085 return _reference; 086 } 087 088 public void setMemory(@Nonnull String memoryName) { 089 Memory memory = InstanceManager.getDefault(MemoryManager.class).getMemory(memoryName); 090 if (memory != null) { 091 setMemory(memory); 092 } else { 093 removeMemory(); 094 log.warn("memory \"{}\" is not found", memoryName); 095 } 096 } 097 098 public void setMemory(@Nonnull NamedBeanHandle<Memory> handle) { 099 _memoryHandle = handle; 100 InstanceManager.memoryManagerInstance().addVetoableChangeListener(this); 101 addRemoveVetoListener(); 102 } 103 104 public void setMemory(@Nonnull Memory memory) { 105 setMemory(InstanceManager.getDefault(NamedBeanHandleManager.class) 106 .getNamedBeanHandle(memory.getDisplayName(), memory)); 107 } 108 109 public void removeMemory() { 110 if (_memoryHandle != null) { 111 _memoryHandle = null; 112 addRemoveVetoListener(); 113 } 114 } 115 116 public NamedBeanHandle<Memory> getMemory() { 117 return _memoryHandle; 118 } 119 120 public void setLocalVariable(@Nonnull String localVariable) { 121 _localVariable = localVariable; 122 } 123 124 public String getLocalVariable() { 125 return _localVariable; 126 } 127 128 public void setFormula(@Nonnull String formula) throws ParserException { 129 _formula = formula; 130 parseFormula(); 131 } 132 133 public String getFormula() { 134 return _formula; 135 } 136 137 private void parseFormula() throws ParserException { 138 if (_addressing == NamedBeanAddressing.Formula) { 139 Map<String, Variable> variables = new HashMap<>(); 140 141 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 142 _expressionNode = parser.parseExpression(_formula); 143 } else { 144 _expressionNode = null; 145 } 146 } 147 148 public LogixNG_SelectTable getSelectTable() { 149 return _selectTable; 150 } 151 152 private void addRemoveVetoListener() { 153 if (_memoryHandle != null) { 154 InstanceManager.getDefault(MemoryManager.class).addVetoableChangeListener(this); 155 } else { 156 InstanceManager.getDefault(MemoryManager.class).removeVetoableChangeListener(this); 157 } 158 } 159 160 public boolean evaluateValue(ConditionalNG conditionalNG) throws JmriException { 161 162 if (_addressing == NamedBeanAddressing.Direct) { 163 return _value; 164 } else { 165 Object val; 166 167 switch (_addressing) { 168 case Reference: 169 val = ReferenceUtil.getReference( 170 conditionalNG.getSymbolTable(), _reference); 171 break; 172 173 case Memory: 174 val = _memoryHandle.getBean().getValue(); 175 break; 176 177 case LocalVariable: 178 SymbolTable symbolNamedBean = conditionalNG.getSymbolTable(); 179 val = symbolNamedBean.getValue(_localVariable); 180 break; 181 182 case Formula: 183 val = _expressionNode != null 184 ? _expressionNode.calculate(conditionalNG.getSymbolTable()) 185 : null; 186 break; 187 188 case Table: 189 val = _selectTable.evaluateTableData(conditionalNG); 190 break; 191 192 default: 193 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 194 } 195 196 return TypeConversionUtil.convertToBoolean(val, false); 197 } 198 } 199 200 public String getDescription(Locale locale) { 201 String enumName; 202 203 String memoryName; 204 if (_memoryHandle != null) { 205 memoryName = _memoryHandle.getName(); 206 } else { 207 memoryName = Bundle.getMessage(locale, "BeanNotSelected"); 208 } 209 210 switch (_addressing) { 211 case Direct: 212 enumName = Bundle.getMessage(locale, "AddressByDirect", 213 _value ? Bundle.getMessage("Boolean_True") : Bundle.getMessage("Boolean_False")); 214 break; 215 216 case Reference: 217 enumName = Bundle.getMessage(locale, "AddressByReference", _reference); 218 break; 219 220 case Memory: 221 enumName = Bundle.getMessage(locale, "AddressByMemory", memoryName); 222 break; 223 224 case LocalVariable: 225 enumName = Bundle.getMessage(locale, "AddressByLocalVariable", _localVariable); 226 break; 227 228 case Formula: 229 enumName = Bundle.getMessage(locale, "AddressByFormula", _formula); 230 break; 231 232 case Table: 233 enumName = Bundle.getMessage( 234 locale, 235 "AddressByTable", 236 _selectTable.getTableNameDescription(locale), 237 _selectTable.getTableRowDescription(locale), 238 _selectTable.getTableColumnDescription(locale)); 239 break; 240 241 default: 242 throw new IllegalArgumentException("invalid _addressing: " + _addressing.name()); 243 } 244 return enumName; 245 } 246 247 @Override 248 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 249 if ("CanDelete".equals(evt.getPropertyName()) && _inUse.isInUse()) { // No I18N 250 if (evt.getOldValue() instanceof Memory) { 251 boolean doVeto = false; 252 if ((_addressing == NamedBeanAddressing.Memory) && (_memoryHandle != null) && evt.getOldValue().equals(_memoryHandle.getBean())) { 253 doVeto = true; 254 } 255 if (doVeto) { 256 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 257 throw new PropertyVetoException(Bundle.getMessage("MemoryInUseMemoryExpressionVeto", _base.getDisplayName()), e); // NOI18N 258 } 259 } 260 } else if ("DoDelete".equals(evt.getPropertyName())) { // No I18N 261 if (evt.getOldValue() instanceof Memory) { 262 if (evt.getOldValue().equals(_memoryHandle.getBean())) { 263 removeMemory(); 264 } 265 } 266 } 267 } 268 269 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNG_SelectBoolean.class); 270}