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; 016import jmri.util.TypeConversionUtil; 017 018/** 019 * This action sets the current report of a Reporter. 020 * 021 * @author Daniel Bergqvist Copyright 2024 022 */ 023public class ActionSetReporter extends AbstractDigitalAction 024 implements PropertyChangeListener { 025 026 private final LogixNG_SelectNamedBean<Reporter> _selectNamedBean = 027 new LogixNG_SelectNamedBean<>( 028 this, Reporter.class, InstanceManager.getDefault(ReporterManager.class), this); 029 private final LogixNG_SelectNamedBean<Memory> _selectOtherMemoryNamedBean = 030 new LogixNG_SelectNamedBean<>( 031 this, Memory.class, InstanceManager.getDefault(MemoryManager.class), this); 032 private ReporterOperation _reporterOperation = ReporterOperation.SetToString; 033 private String _otherConstantValue = ""; 034 private String _otherLocalVariable = ""; 035 private String _otherFormula = ""; 036 private ExpressionNode _otherExpressionNode; 037 private boolean _provideAnIdTag = false; 038 039 private final LogixNG_SelectTable _selectTable = 040 new LogixNG_SelectTable(this, () -> {return _reporterOperation == ReporterOperation.CopyTableCellToReporter;}); 041 042 043 public ActionSetReporter(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 ActionSetReporter copy = new ActionSetReporter(sysName, userName); 055 copy.setComment(getComment()); 056 _selectNamedBean.copy(copy._selectNamedBean); 057 _selectOtherMemoryNamedBean.copy(copy._selectOtherMemoryNamedBean); 058 copy.setMemoryOperation(_reporterOperation); 059 copy.setOtherConstantValue(_otherConstantValue); 060 copy.setOtherLocalVariable(_otherLocalVariable); 061 copy.setOtherFormula(_otherFormula); 062 copy.setProvideAnIdTag(_provideAnIdTag); 063 _selectTable.copy(copy._selectTable); 064 return manager.registerAction(copy); 065 } 066 067 public LogixNG_SelectNamedBean<Reporter> getSelectNamedBean() { 068 return _selectNamedBean; 069 } 070 071 public LogixNG_SelectNamedBean<Memory> getSelectOtherMemoryNamedBean() { 072 return _selectOtherMemoryNamedBean; 073 } 074 075 public void setMemoryOperation(ReporterOperation state) throws ParserException { 076 _reporterOperation = state; 077 parseOtherFormula(); 078 } 079 080 public ReporterOperation getReporterOperation() { 081 return _reporterOperation; 082 } 083 084 // Constant tab 085 public void setOtherConstantValue(String constantValue) { 086 _otherConstantValue = constantValue; 087 } 088 089 public String getConstantValue() { 090 return _otherConstantValue; 091 } 092 093 public LogixNG_SelectTable getSelectTable() { 094 return _selectTable; 095 } 096 097 public void setProvideAnIdTag(boolean createAnIdTag) { 098 this._provideAnIdTag = createAnIdTag; 099 } 100 101 public boolean isProvideAnIdTag() { 102 return _provideAnIdTag; 103 } 104 105 // Variable tab 106 public void setOtherLocalVariable(@Nonnull String localVariable) { 107 assertListenersAreNotRegistered(log, "setOtherLocalVariable"); 108 _otherLocalVariable = localVariable; 109 } 110 111 public String getOtherLocalVariable() { 112 return _otherLocalVariable; 113 } 114 115 // Formula tab 116 public void setOtherFormula(String formula) throws ParserException { 117 _otherFormula = formula; 118 parseOtherFormula(); 119 } 120 121 public String getOtherFormula() { 122 return _otherFormula; 123 } 124 125 private void parseOtherFormula() throws ParserException { 126 if (_reporterOperation == ReporterOperation.CalculateFormula) { 127 Map<String, Variable> variables = new HashMap<>(); 128 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 129 _otherExpressionNode = parser.parseExpression(_otherFormula); 130 } else { 131 _otherExpressionNode = null; 132 } 133 } 134 135 /** {@inheritDoc} */ 136 @Override 137 public Category getCategory() { 138 return Category.ITEM; 139 } 140 141 /** {@inheritDoc} */ 142 @Override 143 public void execute() throws JmriException { 144 145 final ConditionalNG conditionalNG = getConditionalNG(); 146 147 Reporter reporter = _selectNamedBean.evaluateNamedBean(conditionalNG); 148 149 if (reporter == null) { 150// log.warn("memory is null"); 151 return; 152 } 153 154 AtomicReference<JmriException> ref = new AtomicReference<>(); 155 156 ThreadingUtil.runOnLayoutWithJmriException(() -> { 157 158 Object report; 159 160 switch (_reporterOperation) { 161 case SetToNull: 162 report = null; 163 break; 164 165 case SetToString: 166 report = _otherConstantValue; 167 break; 168 169 case CopyTableCellToReporter: 170 report = _selectTable.evaluateTableData(conditionalNG); 171 break; 172 173 case CopyVariableToReporter: 174 report = conditionalNG.getSymbolTable() 175 .getValue(_otherLocalVariable); 176 break; 177 178 case CopyMemoryToReporter: 179 Memory otherMemory = _selectOtherMemoryNamedBean.evaluateNamedBean(conditionalNG); 180 if (otherMemory != null) { 181 report = otherMemory.getValue(); 182 } else { 183 log.warn("setReporter should copy memory to reporter but memory is null"); 184 return; 185 } 186 break; 187 188 case CalculateFormula: 189 if (_otherFormula.isEmpty()) { 190 report = null; 191 } else { 192 try { 193 if (_otherExpressionNode == null) { 194 return; 195 } 196 report = _otherExpressionNode.calculate( 197 conditionalNG.getSymbolTable()); 198 } catch (JmriException e) { 199 ref.set(e); 200 return; 201 } 202 } 203 break; 204 205 default: 206 throw new IllegalArgumentException("_reporterOperation has invalid value: {}" + _reporterOperation.name()); 207 } 208 209 if (_provideAnIdTag) { 210 if (report == null) { 211 throw new IllegalArgumentException("report is null. Can't provide an IdTag"); 212 } 213 IdTag idTag; 214 if (report instanceof IdTag) { 215 idTag = (IdTag)report; 216 } else { 217 String name = TypeConversionUtil.convertToString(report, false); 218 idTag = InstanceManager.getDefault(IdTagManager.class).provideIdTag(name); 219 report = idTag; 220 } 221 idTag.setWhereLastSeen(reporter); 222 } 223 224 reporter.setReport(report); 225 }); 226 227 if (ref.get() != null) throw ref.get(); 228 } 229 230 @Override 231 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 232 throw new UnsupportedOperationException("Not supported."); 233 } 234 235 @Override 236 public int getChildCount() { 237 return 0; 238 } 239 240 @Override 241 public String getShortDescription(Locale locale) { 242 return Bundle.getMessage(locale, "ActionSetReporter_Short"); 243 } 244 245 @Override 246 public String getLongDescription(Locale locale) { 247 String namedBean = _selectNamedBean.getDescription(locale); 248 249 String copyToMemoryName = _selectOtherMemoryNamedBean.getDescription(locale); 250 251 switch (_reporterOperation) { 252 case SetToNull: 253 return Bundle.getMessage(locale, "ActionSetReporter_Long_Null", namedBean); 254 case SetToString: 255 return Bundle.getMessage(locale, "ActionSetReporter_Long_Value", namedBean, _otherConstantValue); 256 case CopyVariableToReporter: 257 return Bundle.getMessage(locale, "ActionSetReporter_Long_CopyVariableToReporter", namedBean, _otherLocalVariable); 258 case CopyMemoryToReporter: 259 return Bundle.getMessage(locale, "ActionSetReporter_Long_CopyMemoryToReporter", namedBean, copyToMemoryName); 260 case CopyTableCellToReporter: 261 String tableName = _selectTable.getTableNameDescription(locale); 262 String rowName = _selectTable.getTableRowDescription(locale); 263 String columnName = _selectTable.getTableColumnDescription(locale); 264 return Bundle.getMessage(locale, "ActionSetReporter_Long_CopyTableCellToReporter", namedBean, tableName, rowName, columnName); 265 case CalculateFormula: 266 return Bundle.getMessage(locale, "ActionSetReporter_Long_Formula", namedBean, _otherFormula); 267 default: 268 throw new IllegalArgumentException("_memoryOperation has invalid value: " + _reporterOperation.name()); 269 } 270 } 271 272 /** {@inheritDoc} */ 273 @Override 274 public void setup() { 275 // Do nothing 276 } 277 278 /** {@inheritDoc} */ 279 @Override 280 public void registerListenersForThisClass() { 281 if (!_listenersAreRegistered) { 282 _selectNamedBean.registerListeners(); 283 _selectOtherMemoryNamedBean.addPropertyChangeListener("value", this); 284 _listenersAreRegistered = true; 285 } 286 } 287 288 /** {@inheritDoc} */ 289 @Override 290 public void unregisterListenersForThisClass() { 291 if (_listenersAreRegistered) { 292 _selectNamedBean.unregisterListeners(); 293 _selectOtherMemoryNamedBean.removePropertyChangeListener("value", this); 294 _listenersAreRegistered = false; 295 } 296 } 297 298 /** {@inheritDoc} */ 299 @Override 300 public void propertyChange(PropertyChangeEvent evt) { 301 getConditionalNG().execute(); 302 } 303 304 /** {@inheritDoc} */ 305 @Override 306 public void disposeMe() { 307 } 308 309 310 public enum ReporterOperation { 311 SetToNull(Bundle.getMessage("ActionSetReporter_ReporterOperation_SetToNull")), 312 SetToString(Bundle.getMessage("ActionSetReporter_ReporterOperation_SetToString")), 313 CopyVariableToReporter(Bundle.getMessage("ActionSetReporter_ReporterOperation_CopyVariableToReporter")), 314 CopyMemoryToReporter(Bundle.getMessage("ActionSetReporter_ReporterOperation_CopyMemoryToReporter")), 315 CopyTableCellToReporter(Bundle.getMessage("ActionSetReporter_ReporterOperation_CopyTableCellToReporter")), 316 CalculateFormula(Bundle.getMessage("ActionSetReporter_ReporterOperation_CalculateFormula")); 317 318 private final String _text; 319 320 private ReporterOperation(String text) { 321 this._text = text; 322 } 323 324 @Override 325 public String toString() { 326 return _text; 327 } 328 329 } 330 331 /** {@inheritDoc} */ 332 @Override 333 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 334 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 335 _selectOtherMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 336 } 337 338 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionSetReporter.class); 339 340}