001package jmri.jmrit.logixng.actions; 002 003import java.beans.*; 004import java.util.*; 005 006import javax.annotation.Nonnull; 007 008import jmri.*; 009import jmri.jmrit.logixng.*; 010import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 011import jmri.jmrit.logixng.util.parser.ParserException; 012 013/** 014 * This action finds a table row or column. 015 * 016 * @author Daniel Bergqvist Copyright 2022 017 */ 018public class ActionFindTableRowOrColumn extends AbstractDigitalAction 019 implements PropertyChangeListener, VetoableChangeListener { 020 021 private final LogixNG_SelectNamedBean<NamedTable> _selectNamedBean = 022 new LogixNG_SelectNamedBean<>( 023 this, NamedTable.class, InstanceManager.getDefault(NamedTableManager.class), this); 024 private TableRowOrColumn _tableRowOrColumn = TableRowOrColumn.Row; 025 private String _rowOrColumnName = ""; 026 private boolean _includeCellsWithoutHeader = false; 027 private String _localVariableNamedBean; 028 private String _localVariableRow; 029 030 public ActionFindTableRowOrColumn(String sys, String user) 031 throws BadUserNameException, BadSystemNameException { 032 super(sys, user); 033 _selectNamedBean.setOnlyDirectAddressingAllowed(); 034 } 035 036 @Override 037 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 038 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 039 String sysName = systemNames.get(getSystemName()); 040 String userName = userNames.get(getSystemName()); 041 if (sysName == null) sysName = manager.getAutoSystemName(); 042 ActionFindTableRowOrColumn copy = new ActionFindTableRowOrColumn(sysName, userName); 043 copy.setComment(getComment()); 044 _selectNamedBean.copy(copy._selectNamedBean); 045 copy.setTableRowOrColumn(_tableRowOrColumn); 046 copy.setRowOrColumnName(_rowOrColumnName); 047 copy.setIncludeCellsWithoutHeader(_includeCellsWithoutHeader); 048 049 copy.setLocalVariableNamedBean(_localVariableNamedBean); 050 copy.setLocalVariableRow(_localVariableRow); 051 052 return manager.registerAction(copy); 053 } 054 055 public LogixNG_SelectNamedBean<NamedTable> getSelectNamedBean() { 056 return _selectNamedBean; 057 } 058 059 /** 060 * Get tableRowOrColumn. 061 * @return tableRowOrColumn 062 */ 063 public TableRowOrColumn getTableRowOrColumn() { 064 return _tableRowOrColumn; 065 } 066 067 /** 068 * Set tableRowOrColumn. 069 * @param tableRowOrColumn tableRowOrColumn 070 */ 071 public void setTableRowOrColumn(@Nonnull TableRowOrColumn tableRowOrColumn) { 072 _tableRowOrColumn = tableRowOrColumn; 073 } 074 075 /** 076 * Get name of row or column 077 * @return name of row or column 078 */ 079 public String getRowOrColumnName() { 080 return _rowOrColumnName; 081 } 082 083 /** 084 * Set name of row or column 085 * @param rowOrColumnName name of row or column 086 */ 087 public void setRowOrColumnName(@Nonnull String rowOrColumnName) { 088 if (rowOrColumnName == null) throw new IllegalArgumentException("Row/column name is null"); 089 _rowOrColumnName = rowOrColumnName; 090 } 091 092 /** 093 * Set whenever to include cells that doesn't have a header. 094 * Cells without headers can be used to use some cells in the table 095 * as comments. 096 * @return true if include cells that doesn't have a header, false otherwise 097 */ 098 public boolean getIncludeCellsWithoutHeader() { 099 return _includeCellsWithoutHeader; 100 } 101 102 /** 103 * Set whenever to include cells that doesn't have a header. 104 * Cells without headers can be used to use some cells in the table 105 * as comments. 106 * @param includeCellsWithoutHeader true if include rows/columns that 107 * doesn't have a header, false otherwise 108 */ 109 public void setIncludeCellsWithoutHeader(boolean includeCellsWithoutHeader) { 110 _includeCellsWithoutHeader = includeCellsWithoutHeader; 111 } 112 113 public void setLocalVariableNamedBean(String localVariableNamedBean) { 114 if ((localVariableNamedBean != null) && (!localVariableNamedBean.isEmpty())) { 115 this._localVariableNamedBean = localVariableNamedBean; 116 } else { 117 this._localVariableNamedBean = null; 118 } 119 } 120 121 public String getLocalVariableNamedBean() { 122 return _localVariableNamedBean; 123 } 124 125 public void setLocalVariableRow(String localVariableNewValue) { 126 if ((localVariableNewValue != null) && (!localVariableNewValue.isEmpty())) { 127 this._localVariableRow = localVariableNewValue; 128 } else { 129 this._localVariableRow = null; 130 } 131 } 132 133 public String getLocalVariableRow() { 134 return _localVariableRow; 135 } 136 137 /** {@inheritDoc} */ 138 @Override 139 public Category getCategory() { 140 return Category.OTHER; 141 } 142 143 private Map<String, Object> getRow(Object value) throws JmriException { 144 if (_selectNamedBean.getNamedBean() == null) { 145 log.error("No table name is given"); 146 return null; // No row found 147 } 148 if (_rowOrColumnName.isEmpty()) { 149 log.error("rowOrColumnName is empty string"); 150 return null; // No row found 151 } 152 153 NamedTable table = _selectNamedBean.evaluateNamedBean(getConditionalNG()); 154 155 if (_tableRowOrColumn == TableRowOrColumn.Row) { 156 int row = table.getRowNumber(_rowOrColumnName); 157 for (int column=1; column <= table.numColumns(); column++) { 158 // If the header is null or empty, treat the row as a comment 159 // unless _includeRowColumnWithoutHeader is true 160 Object header = table.getCell(0, column); 161// System.out.format("Row header: %s%n", header); 162 if (_includeCellsWithoutHeader 163 || ((header != null) && (!header.toString().isEmpty()))) { 164 Object cell = table.getCell(row, column); 165 if ((cell != null) && (cell.equals(value))) { 166 Map<String, Object> rowData = new HashMap<>(); 167 168 for (int rowIndex=1; rowIndex <= table.numRows(); rowIndex++) { 169 Object subHeader = table.getCell(rowIndex, 0); 170 if ((subHeader != null) && (!subHeader.toString().isEmpty())) { 171 rowData.put(subHeader.toString(), table.getCell(rowIndex, column)); 172 } 173 } 174 return rowData; 175 } 176 } 177 } 178 } else { 179 int column = table.getColumnNumber(_rowOrColumnName); 180 for (int row=1; row <= table.numRows(); row++) { 181 // If the header is null or empty, treat the row as a comment 182 // unless _includeRowColumnWithoutHeader is true 183 Object header = table.getCell(row, 0); 184// System.out.format("Column header: %s%n", header); 185 if (_includeCellsWithoutHeader 186 || ((header != null) && (!header.toString().isEmpty()))) { 187 Object cell = table.getCell(row, column); 188 if ((cell != null) && (cell.equals(value))) { 189 Map<String, Object> columnData = new HashMap<>(); 190 191 for (int colIndex=1; colIndex <= table.numColumns(); colIndex++) { 192 Object subHeader = table.getCell(0, colIndex); 193 if ((subHeader != null) && (!subHeader.toString().isEmpty())) { 194 columnData.put(subHeader.toString(), table.getCell(row, colIndex)); 195 } 196 } 197 return columnData; 198 } 199 } 200 } 201 } 202 return null; // No row found 203 } 204 205 /** {@inheritDoc} */ 206 @Override 207 public void execute() throws JmriException { 208 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 209 Object value; 210 if ((_localVariableNamedBean != null) && (_localVariableRow != null)) { 211 value = symbolTable.getValue(_localVariableNamedBean); 212 symbolTable.setValue(_localVariableRow, getRow(value)); 213 } 214 } 215 216 @Override 217 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 218 throw new UnsupportedOperationException("Not supported."); 219 } 220 221 @Override 222 public int getChildCount() { 223 return 0; 224 } 225 226 @Override 227 public String getShortDescription(Locale locale) { 228 return Bundle.getMessage(locale, "ActionFindTableRowOrColumn_Short"); 229 } 230 231 @Override 232 public String getLongDescription(Locale locale) { 233 String tableName = _selectNamedBean.getDescription(locale); 234 return Bundle.getMessage(locale, "ActionFindTableRowOrColumn_Long", 235 _tableRowOrColumn.getOpposite().toStringLowerCase(), 236 tableName, 237 _tableRowOrColumn.toStringLowerCase(), 238 _rowOrColumnName, 239 _tableRowOrColumn.getOpposite().toStringLowerCase(), 240 _localVariableNamedBean); 241 } 242 243 /** {@inheritDoc} */ 244 @Override 245 public void setup() { 246 // Do nothing 247 } 248 249 /** {@inheritDoc} */ 250 @Override 251 public void registerListenersForThisClass() { 252 if (_listenersAreRegistered) return; 253 254 _selectNamedBean.registerListeners(); 255 _listenersAreRegistered = true; 256 } 257 258 /** {@inheritDoc} */ 259 @Override 260 public void unregisterListenersForThisClass() { 261 if (!_listenersAreRegistered) return; 262 263 _selectNamedBean.unregisterListeners(); 264 _listenersAreRegistered = false; 265 } 266 267 /** {@inheritDoc} */ 268 @Override 269 public void propertyChange(PropertyChangeEvent evt) { 270// System.out.format("Table: Property: %s, Bean: %s, Listen: %b%n", evt.getPropertyName(), ((NamedBean)evt.getSource()).getDisplayName(), _listenOnAllProperties); 271 getConditionalNG().execute(); 272 } 273 274 /** {@inheritDoc} */ 275 @Override 276 public void disposeMe() { 277 } 278 279 280 /** {@inheritDoc} */ 281 @Override 282 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 283/* 284 log.debug("getUsageReport :: ActionListenOnBeans: bean = {}, report = {}", cdl, report); 285 for (NamedBeanReference namedBeanReference : _namedBeanReferences.values()) { 286 if (namedBeanReference._handle != null) { 287 if (bean.equals(namedBeanReference._handle.getBean())) { 288 report.add(new NamedBeanUsageReport("LogixNGAction", cdl, getLongDescription())); 289 } 290 } 291 } 292*/ 293 } 294 295 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionFindTableRowOrColumn.class); 296 297}