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