001package jmri.jmrit.logixng.actions; 002 003import java.beans.*; 004import java.util.*; 005import java.util.concurrent.atomic.AtomicReference; 006 007import javax.annotation.Nonnull; 008 009import jmri.*; 010import jmri.jmrit.logixng.*; 011import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 012import jmri.jmrit.logixng.util.parser.*; 013import jmri.jmrit.logixng.util.parser.ExpressionNode; 014import jmri.jmrit.logixng.util.LogixNG_SelectTable; 015import jmri.util.ThreadingUtil; 016 017/** 018 * This action sets the value of a memory. 019 * 020 * @author Daniel Bergqvist Copyright 2018 021 */ 022public class ActionMemory extends AbstractDigitalAction 023 implements PropertyChangeListener { 024 025 private final LogixNG_SelectNamedBean<Memory> _selectNamedBean = 026 new LogixNG_SelectNamedBean<>( 027 this, Memory.class, InstanceManager.getDefault(MemoryManager.class), this); 028 private final LogixNG_SelectNamedBean<Memory> _selectOtherMemoryNamedBean = 029 new LogixNG_SelectNamedBean<>( 030 this, Memory.class, InstanceManager.getDefault(MemoryManager.class), this); 031// private NamedBeanHandle<Memory> _otherMemoryHandle; 032 private MemoryOperation _memoryOperation = MemoryOperation.SetToString; 033 private String _otherConstantValue = ""; 034 private String _otherLocalVariable = ""; 035 private String _otherFormula = ""; 036 private ExpressionNode _otherExpressionNode; 037 private boolean _listenToMemory = true; 038 039 private final LogixNG_SelectTable _selectTable = 040 new LogixNG_SelectTable(this, () -> {return _memoryOperation == MemoryOperation.CopyTableCellToMemory;}); 041 042 043 public ActionMemory(String sys, String user) 044 throws BadUserNameException, BadSystemNameException { 045 super(sys, user); 046 } 047 048 @Override 049 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 050 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 051 String sysName = systemNames.get(getSystemName()); 052 String userName = userNames.get(getSystemName()); 053 if (sysName == null) sysName = manager.getAutoSystemName(); 054 ActionMemory copy = new ActionMemory(sysName, userName); 055 copy.setComment(getComment()); 056 _selectNamedBean.copy(copy._selectNamedBean); 057 _selectOtherMemoryNamedBean.copy(copy._selectOtherMemoryNamedBean); 058 copy.setMemoryOperation(_memoryOperation); 059 copy.setOtherConstantValue(_otherConstantValue); 060// copy.setOtherTableCell(_otherTableCell); 061 copy.setOtherLocalVariable(_otherLocalVariable); 062 copy.setOtherFormula(_otherFormula); 063 copy.setListenToMemory(_listenToMemory); 064 _selectTable.copy(copy._selectTable); 065 return manager.registerAction(copy); 066 } 067 068 public LogixNG_SelectNamedBean<Memory> getSelectNamedBean() { 069 return _selectNamedBean; 070 } 071 072 public LogixNG_SelectNamedBean<Memory> getSelectOtherMemoryNamedBean() { 073 return _selectOtherMemoryNamedBean; 074 } 075 076 public void setMemoryOperation(MemoryOperation state) throws ParserException { 077 _memoryOperation = state; 078 parseOtherFormula(); 079 } 080 081 public MemoryOperation getMemoryOperation() { 082 return _memoryOperation; 083 } 084 085 // Constant tab 086 public void setOtherConstantValue(String constantValue) { 087 _otherConstantValue = constantValue; 088 } 089 090 public String getConstantValue() { 091 return _otherConstantValue; 092 } 093 094 public LogixNG_SelectTable getSelectTable() { 095 return _selectTable; 096 } 097 098 public void setListenToMemory(boolean listenToMemory) { 099 this._listenToMemory = listenToMemory; 100 } 101 102 public boolean getListenToMemory() { 103 return _listenToMemory; 104 } 105 106 // Variable tab 107 public void setOtherLocalVariable(@Nonnull String localVariable) { 108 assertListenersAreNotRegistered(log, "setOtherLocalVariable"); 109 _otherLocalVariable = localVariable; 110 } 111 112 public String getOtherLocalVariable() { 113 return _otherLocalVariable; 114 } 115 116 // Formula tab 117 public void setOtherFormula(String formula) throws ParserException { 118 _otherFormula = formula; 119 parseOtherFormula(); 120 } 121 122 public String getOtherFormula() { 123 return _otherFormula; 124 } 125 126 private void parseOtherFormula() throws ParserException { 127 if (_memoryOperation == MemoryOperation.CalculateFormula) { 128 Map<String, Variable> variables = new HashMap<>(); 129 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 130 _otherExpressionNode = parser.parseExpression(_otherFormula); 131 } else { 132 _otherExpressionNode = null; 133 } 134 } 135 136 /** {@inheritDoc} */ 137 @Override 138 public Category getCategory() { 139 return Category.ITEM; 140 } 141 142 /** {@inheritDoc} */ 143 @Override 144 public void execute() throws JmriException { 145 146 final ConditionalNG conditionalNG = getConditionalNG(); 147 148 Memory memory = _selectNamedBean.evaluateNamedBean(conditionalNG); 149 150 if (memory == null) { 151// log.warn("memory is null"); 152 return; 153 } 154 155 AtomicReference<JmriException> ref = new AtomicReference<>(); 156 157 ThreadingUtil.runOnLayoutWithJmriException(() -> { 158 159 switch (_memoryOperation) { 160 case SetToNull: 161 memory.setValue(null); 162 break; 163 164 case SetToString: 165 memory.setValue(_otherConstantValue); 166 break; 167 168 case CopyTableCellToMemory: 169 Object value = _selectTable.evaluateTableData(conditionalNG); 170 memory.setValue(value); 171 break; 172 173 case CopyVariableToMemory: 174 Object variableValue = conditionalNG 175 .getSymbolTable().getValue(_otherLocalVariable); 176 memory.setValue(variableValue); 177 break; 178 179 case CopyMemoryToMemory: 180 Memory otherMemory = _selectOtherMemoryNamedBean.evaluateNamedBean(conditionalNG); 181 if (otherMemory != null) { 182 memory.setValue(otherMemory.getValue()); 183 } else { 184 log.warn("setMemory should copy memory to memory but other memory is null"); 185 } 186 break; 187 188 case CalculateFormula: 189 if (_otherFormula.isEmpty()) { 190 memory.setValue(null); 191 } else { 192 try { 193 if (_otherExpressionNode == null) { 194 return; 195 } 196 memory.setValue(_otherExpressionNode.calculate( 197 conditionalNG.getSymbolTable())); 198 } catch (JmriException e) { 199 ref.set(e); 200 } 201 } 202 break; 203 204 default: 205 throw new IllegalArgumentException("_memoryOperation has invalid value: {}" + _memoryOperation.name()); 206 } 207 }); 208 209 if (ref.get() != null) throw ref.get(); 210 } 211 212 @Override 213 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 214 throw new UnsupportedOperationException("Not supported."); 215 } 216 217 @Override 218 public int getChildCount() { 219 return 0; 220 } 221 222 @Override 223 public String getShortDescription(Locale locale) { 224 return Bundle.getMessage(locale, "ActionMemory_Short"); 225 } 226 227 @Override 228 public String getLongDescription(Locale locale) { 229 String namedBean = _selectNamedBean.getDescription(locale); 230 231 String copyToMemoryName = _selectOtherMemoryNamedBean.getDescription(locale); 232 233 switch (_memoryOperation) { 234 case SetToNull: 235 return Bundle.getMessage(locale, "ActionMemory_Long_Null", namedBean); 236 case SetToString: 237 return Bundle.getMessage(locale, "ActionMemory_Long_Value", namedBean, _otherConstantValue); 238 case CopyVariableToMemory: 239 return Bundle.getMessage(locale, "ActionMemory_Long_CopyVariableToMemory", namedBean, _otherLocalVariable); 240 case CopyMemoryToMemory: 241 return Bundle.getMessage(locale, "ActionMemory_Long_CopyMemoryToMemory", namedBean, copyToMemoryName); 242 case CopyTableCellToMemory: 243 String tableName = _selectTable.getTableNameDescription(locale); 244 String rowName = _selectTable.getTableRowDescription(locale); 245 String columnName = _selectTable.getTableColumnDescription(locale); 246 return Bundle.getMessage(locale, "ActionMemory_Long_CopyTableCellToMemory", namedBean, tableName, rowName, columnName); 247 case CalculateFormula: 248 return Bundle.getMessage(locale, "ActionMemory_Long_Formula", namedBean, _otherFormula); 249 default: 250 throw new IllegalArgumentException("_memoryOperation has invalid value: " + _memoryOperation.name()); 251 } 252 } 253 254 /** {@inheritDoc} */ 255 @Override 256 public void setup() { 257 // Do nothing 258 } 259 260 /** {@inheritDoc} */ 261 @Override 262 public void registerListenersForThisClass() { 263 if (!_listenersAreRegistered) { 264 if (_listenToMemory) { 265 _selectOtherMemoryNamedBean.addPropertyChangeListener("value", this); 266 } 267 _selectNamedBean.registerListeners(); 268 _listenersAreRegistered = true; 269 } 270 } 271 272 /** {@inheritDoc} */ 273 @Override 274 public void unregisterListenersForThisClass() { 275 if (_listenersAreRegistered && _listenToMemory) { 276 _selectOtherMemoryNamedBean.removePropertyChangeListener("value", this); 277 } 278 _selectNamedBean.unregisterListeners(); 279 _listenersAreRegistered = false; 280 } 281 282 /** {@inheritDoc} */ 283 @Override 284 public void propertyChange(PropertyChangeEvent evt) { 285 getConditionalNG().execute(); 286 } 287 288 /** {@inheritDoc} */ 289 @Override 290 public void disposeMe() { 291 } 292 293 294 public enum MemoryOperation { 295 SetToNull(Bundle.getMessage("ActionMemory_MemoryOperation_SetToNull")), 296 SetToString(Bundle.getMessage("ActionMemory_MemoryOperation_SetToString")), 297 CopyVariableToMemory(Bundle.getMessage("ActionMemory_MemoryOperation_CopyVariableToMemory")), 298 CopyMemoryToMemory(Bundle.getMessage("ActionMemory_MemoryOperation_CopyMemoryToMemory")), 299 CopyTableCellToMemory(Bundle.getMessage("ActionMemory_MemoryOperation_CopyTableCellToMemory")), 300 CalculateFormula(Bundle.getMessage("ActionMemory_MemoryOperation_CalculateFormula")); 301 302 private final String _text; 303 304 private MemoryOperation(String text) { 305 this._text = text; 306 } 307 308 @Override 309 public String toString() { 310 return _text; 311 } 312 313 } 314 315 /** {@inheritDoc} */ 316 @Override 317 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 318 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 319 _selectOtherMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 320 } 321 322 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionMemory.class); 323 324}