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