001package jmri.jmrit.logixng.actions; 002 003import java.beans.*; 004import java.util.*; 005 006import jmri.*; 007import jmri.jmrit.logixng.*; 008import jmri.jmrit.logixng.util.ReferenceUtil; 009import jmri.jmrit.logixng.util.parser.*; 010import jmri.jmrit.logixng.util.parser.ExpressionNode; 011import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 012import jmri.script.swing.ScriptOutput; 013 014/** 015 * This action logs some data. 016 * 017 * @author Daniel Bergqvist Copyright 2021 018 */ 019public class LogData extends AbstractDigitalAction 020 implements PropertyChangeListener, VetoableChangeListener { 021 022 private boolean _logToLog = true; 023 private boolean _logToScriptOutput = false; 024 private FormatType _formatType = FormatType.OnlyText; 025 private String _format = ""; 026 private final List<Data> _dataList = new ArrayList<>(); 027 028 public LogData(String sys, String user) 029 throws BadUserNameException, BadSystemNameException { 030 super(sys, user); 031 } 032 033 @Override 034 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 035 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 036 String sysName = systemNames.get(getSystemName()); 037 String userName = userNames.get(getSystemName()); 038 if (sysName == null) sysName = manager.getAutoSystemName(); 039 LogData copy = new LogData(sysName, userName); 040 copy.setComment(getComment()); 041 copy.setLogToLog(_logToLog); 042 copy.setLogToScriptOutput(_logToScriptOutput); 043 copy.setFormat(_format); 044 copy.setFormatType(_formatType); 045 for (Data data : _dataList) { 046 copy.getDataList().add(new Data(data)); 047 } 048 return manager.registerAction(copy); 049 } 050 051 public void setLogToLog(boolean logToLog) { 052 _logToLog = logToLog; 053 } 054 055 public boolean getLogToLog() { 056 return _logToLog; 057 } 058 059 public void setLogToScriptOutput(boolean logToScriptOutput) { 060 _logToScriptOutput = logToScriptOutput; 061 } 062 063 public boolean getLogToScriptOutput() { 064 return _logToScriptOutput; 065 } 066 067 public void setFormatType(FormatType formatType) { 068 _formatType = formatType; 069 } 070 071 public FormatType getFormatType() { 072 return _formatType; 073 } 074 075 public void setFormat(String format) { 076 _format = format; 077 } 078 079 public String getFormat() { 080 return _format; 081 } 082 083 public List<Data> getDataList() { 084 return _dataList; 085 } 086 087 @Override 088 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 089/* 090 if ("CanDelete".equals(evt.getPropertyName())) { // No I18N 091 if (evt.getOldValue() instanceof Memory) { 092 if (evt.getOldValue().equals(getMemory().getBean())) { 093 throw new PropertyVetoException(getDisplayName(), evt); 094 } 095 } 096 } else if ("DoDelete".equals(evt.getPropertyName())) { // No I18N 097 if (evt.getOldValue() instanceof Memory) { 098 if (evt.getOldValue().equals(getMemory().getBean())) { 099 setMemory((Memory)null); 100 } 101 } 102 } 103*/ 104 } 105 106 /** {@inheritDoc} */ 107 @Override 108 public Category getCategory() { 109 return Category.OTHER; 110 } 111 112 private List<Object> getDataValues() throws JmriException { 113 List<Object> values = new ArrayList<>(); 114 for (Data _data : _dataList) { 115 switch (_data._dataType) { 116 case LocalVariable: 117 values.add(getConditionalNG().getSymbolTable().getValue(_data._data)); 118 break; 119 120 case Memory: 121 MemoryManager memoryManager = InstanceManager.getDefault(MemoryManager.class); 122 Memory memory = memoryManager.getMemory(_data._data); 123 if (memory == null) throw new IllegalArgumentException("Memory '" + _data._data + "' not found"); 124 values.add(memory.getValue()); 125 break; 126 127 case Reference: 128 values.add(ReferenceUtil.getReference( 129 getConditionalNG().getSymbolTable(), _data._data)); 130 break; 131 132 case Formula: 133 if (_data._expressionNode != null) { 134 values.add(_data._expressionNode.calculate(getConditionalNG().getSymbolTable())); 135 } 136 137 break; 138 139 default: 140 throw new IllegalArgumentException("_formatType has invalid value: "+_formatType.name()); 141 } 142 } 143 return values; 144 } 145 146 /** {@inheritDoc} */ 147 @Override 148 public void execute() throws JmriException { 149 150 String str; 151 152 switch (_formatType) { 153 case OnlyText: 154 str = _format; 155 break; 156 157 case CommaSeparatedList: 158 StringBuilder sb = new StringBuilder(); 159 for (Object value : getDataValues()) { 160 if (sb.length() > 0) sb.append(", "); 161 sb.append(value != null ? value.toString() : "null"); 162 } 163 str = sb.toString(); 164 break; 165 166 case StringFormat: 167 str = String.format(_format, getDataValues().toArray()); 168 break; 169 170 default: 171 throw new IllegalArgumentException("_formatType has invalid value: "+_formatType.name()); 172 } 173 doLogging(str); 174 } 175 176 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 177 justification="Pass generated String unchanged") 178 private void doLogging(String logString) { 179 if (_logToLog) { 180 log.warn(logString); 181 } 182 if (_logToScriptOutput) { 183 ScriptOutput.getDefault().getOutputArea().append(logString+"\n"); 184 } 185 } 186 187 @Override 188 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 189 throw new UnsupportedOperationException("Not supported."); 190 } 191 192 @Override 193 public int getChildCount() { 194 return 0; 195 } 196 197 @Override 198 public String getShortDescription(Locale locale) { 199 return Bundle.getMessage(locale, "LogData_Short"); 200 } 201 202 @Override 203 public String getLongDescription(Locale locale) { 204 String bundleKey; 205 switch (_formatType) { 206 case OnlyText: 207 bundleKey = "LogData_Long_TextOnly"; 208 break; 209 case CommaSeparatedList: 210 bundleKey = "LogData_Long_CommaSeparatedList"; 211 break; 212 case StringFormat: 213 bundleKey = "LogData_Long_StringFormat"; 214 break; 215 default: 216 throw new RuntimeException("_formatType has unknown value: "+_formatType.name()); 217 } 218 return Bundle.getMessage(locale, bundleKey, _format); 219 } 220 221 /** {@inheritDoc} */ 222 @Override 223 public void setup() { 224 // Do nothing 225 } 226 227 /** {@inheritDoc} */ 228 @Override 229 public void registerListenersForThisClass() { 230 // Do nothing 231 } 232 233 /** {@inheritDoc} */ 234 @Override 235 public void unregisterListenersForThisClass() { 236 // Do nothing 237 } 238 239 /** {@inheritDoc} */ 240 @Override 241 public void propertyChange(PropertyChangeEvent evt) { 242 getConditionalNG().execute(); 243 } 244 245 /** {@inheritDoc} */ 246 @Override 247 public void disposeMe() { 248 } 249 250 251 /** {@inheritDoc} */ 252 @Override 253 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 254/* 255 log.debug("getUsageReport :: LogData: bean = {}, report = {}", cdl, report); 256 for (NamedBeanReference namedBeanReference : _namedBeanReferences.values()) { 257 if (namedBeanReference._handle != null) { 258 if (bean.equals(namedBeanReference._handle.getBean())) { 259 report.add(new NamedBeanUsageReport("LogixNGAction", cdl, getLongDescription())); 260 } 261 } 262 } 263*/ 264 } 265 266 267 public enum FormatType { 268 OnlyText(Bundle.getMessage("LogData_FormatType_TextOnly"), true, false), 269 CommaSeparatedList(Bundle.getMessage("LogData_FormatType_CommaSeparatedList"), false, true), 270 StringFormat(Bundle.getMessage("LogData_FormatType_StringFormat"), true, true); 271 272 private final String _text; 273 private final boolean _useFormat; 274 private final boolean _useData; 275 276 private FormatType(String text, boolean useFormat, boolean useData) { 277 this._text = text; 278 this._useFormat = useFormat; 279 this._useData = useData; 280 } 281 282 @Override 283 public String toString() { 284 return _text; 285 } 286 287 public boolean getUseFormat() { 288 return _useFormat; 289 } 290 291 public boolean getUseData() { 292 return _useData; 293 } 294 295 } 296 297 298 public enum DataType { 299 LocalVariable(Bundle.getMessage("LogData_Operation_LocalVariable")), 300 Memory(Bundle.getMessage("LogData_Operation_Memory")), 301 Reference(Bundle.getMessage("LogData_Operation_Reference")), 302 Formula(Bundle.getMessage("LogData_Operation_Formula")); 303 304 private final String _text; 305 306 private DataType(String text) { 307 this._text = text; 308 } 309 310 @Override 311 public String toString() { 312 return _text; 313 } 314 315 } 316 317 318 public static class Data { 319 320 private DataType _dataType = DataType.LocalVariable; 321 private String _data = ""; 322 private ExpressionNode _expressionNode; 323 324 public Data(Data data) throws ParserException { 325 _dataType = data._dataType; 326 _data = data._data; 327 calculateFormula(); 328 } 329 330 public Data(DataType dataType, String data) throws ParserException { 331 _dataType = dataType; 332 _data = data; 333 calculateFormula(); 334 } 335 336 private void calculateFormula() throws ParserException { 337 if (_dataType == DataType.Formula) { 338 Map<String, Variable> variables = new HashMap<>(); 339 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 340 _expressionNode = parser.parseExpression(_data); 341 } else { 342 _expressionNode = null; 343 } 344 } 345 346 public void setDataType(DataType dataType) { _dataType = dataType; } 347 public DataType getDataType() { return _dataType; } 348 349 public void setData(String data) { _data = data; } 350 public String getData() { return _data; } 351 352 } 353 354 355 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogData.class); 356 357}