001package jmri.jmrit.logixng.actions; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.*; 006 007import javax.annotation.Nonnull; 008 009import jmri.*; 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 action triggers a reporter. 020 * 021 * @author Daniel Bergqvist Copyright 2021 022 * @author Dave Sand Copyright 2021 023 */ 024public class ActionReporter extends AbstractDigitalAction 025 implements PropertyChangeListener { 026 027 private final LogixNG_SelectNamedBean<Reporter> _selectNamedBean = 028 new LogixNG_SelectNamedBean<>( 029 this, Reporter.class, InstanceManager.getDefault(ReporterManager.class), this); 030 031 private final LogixNG_SelectNamedBean<Memory> _selectMemoryNamedBean = 032 new LogixNG_SelectNamedBean<>( 033 this, Memory.class, InstanceManager.getDefault(MemoryManager.class), this); 034 035 private ReporterValue _reporterValue = ReporterValue.CopyCurrentReport; 036 037 private NamedBeanAddressing _dataAddressing = NamedBeanAddressing.Direct; 038 private String _dataReference = ""; 039 private String _dataLocalVariable = ""; 040 private String _dataFormula = ""; 041 private ExpressionNode _dataExpressionNode; 042 043// private NamedBeanHandle<Memory> _memoryHandle; 044 045 public ActionReporter(String sys, String user) 046 throws BadUserNameException, BadSystemNameException { 047 super(sys, user); 048 } 049 050 @Override 051 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 052 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 053 String sysName = systemNames.get(getSystemName()); 054 String userName = userNames.get(getSystemName()); 055 if (sysName == null) sysName = manager.getAutoSystemName(); 056 ActionReporter copy = new ActionReporter(sysName, userName); 057 copy.setComment(getComment()); 058 _selectNamedBean.copy(copy._selectNamedBean); 059 _selectMemoryNamedBean.copy(copy._selectMemoryNamedBean); 060 061 copy.setReporterValue(_reporterValue); 062 063 copy.setDataAddressing(_dataAddressing); 064 copy.setDataReference(_dataReference); 065 copy.setDataLocalVariable(_dataLocalVariable); 066 copy.setDataFormula(_dataFormula); 067// if (_memoryHandle != null) copy.setMemory(_memoryHandle); 068 069 return manager.registerAction(copy); 070 } 071 072 public LogixNG_SelectNamedBean<Reporter> getSelectNamedBean() { 073 return _selectNamedBean; 074 } 075 076 public LogixNG_SelectNamedBean<Memory> getSelectMemoryNamedBean() { 077 return _selectMemoryNamedBean; 078 } 079 080 public void setReporterValue(ReporterValue value) { 081 _reporterValue = value; 082 } 083 084 public ReporterValue getReporterValue() { 085 return _reporterValue; 086 } 087 088 public void setDataAddressing(NamedBeanAddressing addressing) throws ParserException { 089 _dataAddressing = addressing; 090 parseDataFormula(); 091 } 092 093 public NamedBeanAddressing getDataAddressing() { 094 return _dataAddressing; 095 } 096 097 public void setDataReference(@Nonnull String reference) { 098 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 099 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 100 } 101 _dataReference = reference; 102 } 103 104 public String getDataReference() { 105 return _dataReference; 106 } 107 108 public void setDataLocalVariable(@Nonnull String localVariable) { 109 _dataLocalVariable = localVariable; 110 } 111 112 public String getDataLocalVariable() { 113 return _dataLocalVariable; 114 } 115 116 public void setDataFormula(@Nonnull String formula) throws ParserException { 117 _dataFormula = formula; 118 parseDataFormula(); 119 } 120 121 public String getDataFormula() { 122 return _dataFormula; 123 } 124 125 private void parseDataFormula() throws ParserException { 126 if (_dataAddressing == NamedBeanAddressing.Formula) { 127 Map<String, Variable> variables = new HashMap<>(); 128 129 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 130 _dataExpressionNode = parser.parseExpression(_dataFormula); 131 } else { 132 _dataExpressionNode = null; 133 } 134 } 135 136 137 /** {@inheritDoc} */ 138 @Override 139 public Category getCategory() { 140 return Category.ITEM; 141 } 142 143 Object getReporterData(Reporter reporter) throws JmriException { 144 Object obj; 145 ReporterValue value = _reporterValue; 146 147 switch (value) { 148 case CopyCurrentReport: 149 obj = reporter.getCurrentReport(); 150 break; 151 case CopyLastReport: 152 obj = reporter.getLastReport(); 153 break; 154 case CopyState: 155 obj = reporter.getState(); 156 break; 157 default: 158 throw new IllegalArgumentException("invalid value name: " + value.name()); 159 } 160 161 return obj; 162 } 163 164 void updateDestination(Object data) throws JmriException { 165 switch (_dataAddressing) { 166 case Direct: 167 Memory memory = _selectMemoryNamedBean.evaluateNamedBean(getConditionalNG()); 168 if (memory != null) { 169 memory.setValue(data); 170 } 171 break; 172 173 case Reference: 174 String refName = ReferenceUtil.getReference( 175 getConditionalNG().getSymbolTable(), _dataReference); 176 log.debug("ref ref = {}, name = {}", _dataReference, refName); 177 Memory refMem = InstanceManager.getDefault(MemoryManager.class).getMemory(refName); 178 if (refMem == null) { 179 throw new IllegalArgumentException("invalid memory reference: " + refName); 180 } 181 refMem.setValue(data); 182 break; 183 184 case LocalVariable: 185 log.debug("LocalVariable: lv = {}", _dataLocalVariable); 186 getConditionalNG().getSymbolTable().setValue(_dataLocalVariable, data); 187 break; 188 189 case Formula: 190 String formulaName = _dataExpressionNode != null 191 ? TypeConversionUtil.convertToString( 192 _dataExpressionNode.calculate( 193 getConditionalNG().getSymbolTable()), false) 194 : null; 195 if (formulaName == null) { 196 throw new IllegalArgumentException("invalid memory formula, name is null"); 197 } 198 199 Memory formulaMem = InstanceManager.getDefault(MemoryManager.class).getMemory(formulaName); 200 if (formulaMem == null) { 201 throw new IllegalArgumentException("invalid memory formula: " + formulaName); 202 } 203 formulaMem.setValue(data); 204 break; 205 206 default: 207 throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name()); 208 } 209 } 210 211 /** {@inheritDoc} */ 212 @Override 213 public void execute() throws JmriException { 214 // Get the reporter bean 215 Reporter reporter = _selectNamedBean.evaluateNamedBean(getConditionalNG()); 216 if (reporter == null) return; 217 log.debug("reporter = {}", reporter.getDisplayName()); 218 219 // Get the reporter data 220 Object data = getReporterData(reporter); 221 log.debug("data = {}", data); 222 223 // Update the destination 224 updateDestination(data); 225 } 226 227 @Override 228 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 229 throw new UnsupportedOperationException("Not supported."); 230 } 231 232 @Override 233 public int getChildCount() { 234 return 0; 235 } 236 237 @Override 238 public String getShortDescription(Locale locale) { 239 return Bundle.getMessage(locale, "ActionReporter_Short"); 240 } 241 242 @Override 243 public String getLongDescription(Locale locale) { 244 String bean = _selectNamedBean.getDescription(locale); 245 String dest = ""; 246 247 switch (_dataAddressing) { 248 case Direct: 249 String memoryName = _selectMemoryNamedBean.getDescription(locale); 250 dest = Bundle.getMessage(locale, "AddressByDirect", memoryName); 251 break; 252 253 case Reference: 254 dest = Bundle.getMessage(locale, "AddressByReference", _dataReference); 255 break; 256 257 case LocalVariable: 258 dest = Bundle.getMessage(locale, "AddressByLocalVariable", _dataLocalVariable); 259 break; 260 261 case Formula: 262 dest = Bundle.getMessage(locale, "AddressByFormula", _dataFormula); 263 break; 264 265 default: 266 throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name()); 267 } 268 269 String item = getReporterValue().toString(); 270 271 return Bundle.getMessage(locale, "ActionReporter_Long", item, bean, dest); 272 } 273 274 /** {@inheritDoc} */ 275 @Override 276 public void setup() { 277 // Do nothing 278 } 279 280 /** {@inheritDoc} */ 281 @Override 282 public void registerListenersForThisClass() { 283 _selectNamedBean.registerListeners(); 284 } 285 286 /** {@inheritDoc} */ 287 @Override 288 public void unregisterListenersForThisClass() { 289 _selectNamedBean.unregisterListeners(); 290 } 291 292 /** {@inheritDoc} */ 293 @Override 294 public void disposeMe() { 295 } 296 297 public enum ReporterValue { 298 CopyCurrentReport(Bundle.getMessage("ActionReporter_CopyCurrentReport")), 299 CopyLastReport(Bundle.getMessage("ActionReporter_CopyLastReport")), 300 CopyState(Bundle.getMessage("ActionReporter_CopyState")); 301 302 private final String _text; 303 304 private ReporterValue(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 _selectMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 320 } 321 322 /** {@inheritDoc} */ 323 @Override 324 public void propertyChange(PropertyChangeEvent evt) { 325 getConditionalNG().execute(); 326 } 327 328 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionReporter.class); 329 330}