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 an enum value for LogixNG actions and expressions. 021 * 022 * @param <E> the type of enum 023 * 024 * @author Daniel Bergqvist (C) 2022 025 */ 026public class LogixNG_SelectEnum<E extends Enum<?>> implements VetoableChangeListener { 027 028 private final AbstractBase _base; 029 private final InUse _inUse; 030 private final E[] _enumArray; 031 private final LogixNG_SelectTable _selectTable; 032 033 private NamedBeanAddressing _addressing = NamedBeanAddressing.Direct; 034 private E _enum; 035 private String _reference = ""; 036 private NamedBeanHandle<Memory> _memoryHandle; 037 private String _localVariable = ""; 038 private String _formula = ""; 039 private ExpressionNode _expressionNode; 040 041 042 public LogixNG_SelectEnum(AbstractBase base, E[] enumArray, E initialEnum) { 043 _base = base; 044 _inUse = () -> true; 045 _enumArray = enumArray; 046 _enum = initialEnum; 047 _selectTable = new LogixNG_SelectTable(_base, _inUse); 048 } 049 050 051 public void copy(LogixNG_SelectEnum<E> copy) throws ParserException { 052 copy.setAddressing(_addressing); 053 copy.setEnum(_enum); 054 copy.setLocalVariable(_localVariable); 055 copy.setReference(_reference); 056 copy.setMemory(_memoryHandle); 057 copy.setFormula(_formula); 058 _selectTable.copy(copy._selectTable); 059 } 060 061 public void setAddressing(@Nonnull NamedBeanAddressing addressing) throws ParserException { 062 this._addressing = addressing; 063 parseFormula(); 064 } 065 066 public boolean isDirectAddressing() { 067 return _addressing == NamedBeanAddressing.Direct; 068 } 069 070 public NamedBeanAddressing getAddressing() { 071 return _addressing; 072 } 073 074 public void setEnum(@Nonnull E e) { 075 _base.assertListenersAreNotRegistered(log, "setEnum"); 076 _enum = e; 077 } 078 079 public boolean isEnum(E e) { 080 return _addressing == NamedBeanAddressing.Direct && _enum == e; 081 } 082 083 public E getEnum() { 084 return _enum; 085 } 086 087 public E getEnum(String name) { 088 for (E e : _enumArray) { 089 if (e.name().equals(name)) return e; 090 } 091 return null; 092 } 093 094 public void setReference(@Nonnull String reference) { 095 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 096 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 097 } 098 _reference = reference; 099 } 100 101 public String getReference() { 102 return _reference; 103 } 104 105 public void setMemory(@Nonnull String memoryName) { 106 Memory memory = InstanceManager.getDefault(MemoryManager.class).getMemory(memoryName); 107 if (memory != null) { 108 setMemory(memory); 109 } else { 110 removeMemory(); 111 log.warn("memory \"{}\" is not found", memoryName); 112 } 113 } 114 115 public void setMemory(@Nonnull NamedBeanHandle<Memory> handle) { 116 _memoryHandle = handle; 117 InstanceManager.memoryManagerInstance().addVetoableChangeListener(this); 118 addRemoveVetoListener(); 119 } 120 121 public void setMemory(@Nonnull Memory memory) { 122 setMemory(InstanceManager.getDefault(NamedBeanHandleManager.class) 123 .getNamedBeanHandle(memory.getDisplayName(), memory)); 124 } 125 126 public void removeMemory() { 127 if (_memoryHandle != null) { 128 _memoryHandle = null; 129 addRemoveVetoListener(); 130 } 131 } 132 133 public NamedBeanHandle<Memory> getMemory() { 134 return _memoryHandle; 135 } 136 137 public void setLocalVariable(@Nonnull String localVariable) { 138 _localVariable = localVariable; 139 } 140 141 public String getLocalVariable() { 142 return _localVariable; 143 } 144 145 public void setFormula(@Nonnull String formula) throws ParserException { 146 _formula = formula; 147 parseFormula(); 148 } 149 150 public String getFormula() { 151 return _formula; 152 } 153 154 private void parseFormula() throws ParserException { 155 if (_addressing == NamedBeanAddressing.Formula) { 156 Map<String, Variable> variables = new HashMap<>(); 157 158 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 159 _expressionNode = parser.parseExpression(_formula); 160 } else { 161 _expressionNode = null; 162 } 163 } 164 165 public LogixNG_SelectTable getSelectTable() { 166 return _selectTable; 167 } 168 169 private void addRemoveVetoListener() { 170 if (_memoryHandle != null) { 171 InstanceManager.getDefault(MemoryManager.class).addVetoableChangeListener(this); 172 } else { 173 InstanceManager.getDefault(MemoryManager.class).removeVetoableChangeListener(this); 174 } 175 } 176 177 public E evaluateEnum(ConditionalNG conditionalNG) throws JmriException { 178 179 if (_addressing == NamedBeanAddressing.Direct) { 180 return _enum; 181 } else { 182 String name; 183 184 switch (_addressing) { 185 case Reference: 186 name = ReferenceUtil.getReference( 187 conditionalNG.getSymbolTable(), _reference); 188 break; 189 190 case Memory: 191 name = TypeConversionUtil 192 .convertToString(_memoryHandle.getBean().getValue(), false); 193 break; 194 195 case LocalVariable: 196 SymbolTable symbolNamedBean = conditionalNG.getSymbolTable(); 197 name = TypeConversionUtil 198 .convertToString(symbolNamedBean.getValue(_localVariable), false); 199 break; 200 201 case Formula: 202 name = _expressionNode != null 203 ? TypeConversionUtil.convertToString( 204 _expressionNode.calculate( 205 conditionalNG.getSymbolTable()), false) 206 : null; 207 break; 208 209 case Table: 210 name = TypeConversionUtil.convertToString( 211 _selectTable.evaluateTableData(conditionalNG), false); 212 break; 213 214 default: 215 throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name()); 216 } 217 218 return getEnum(name); 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", _enum.toString()); 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_SelectEnum.class); 291}