001package jmri.jmrit.logixng.actions; 002 003import jmri.jmrit.logixng.NamedBeanType; 004 005import java.beans.*; 006import java.util.*; 007 008import javax.annotation.Nonnull; 009 010import jmri.*; 011import jmri.jmrit.logixng.*; 012import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 013import jmri.jmrit.logixng.util.parser.ParserException; 014 015/** 016 * This action listens on some beans and runs the ConditionalNG on property change. 017 * 018 * @author Daniel Bergqvist Copyright 2022 019 */ 020public class ActionCreateBeansFromTable extends AbstractDigitalAction 021 implements PropertyChangeListener, VetoableChangeListener { 022 023 private boolean _onlyCreatableTypes = true; 024 private NamedBeanType _namedBeanType = NamedBeanType.Light; 025 private final LogixNG_SelectNamedBean<NamedTable> _selectNamedBean = 026 new LogixNG_SelectNamedBean<>( 027 this, NamedTable.class, InstanceManager.getDefault(NamedTableManager.class), this); 028 private TableRowOrColumn _tableRowOrColumn = TableRowOrColumn.Row; 029 private String _rowOrColumnSystemName = ""; 030 private String _rowOrColumnUserName = ""; 031 private boolean _includeCellsWithoutHeader = false; 032 private final List<Map.Entry<NamedBean, String>> _namedBeansEntries = new ArrayList<>(); 033 private boolean _moveUserName = false; 034 private boolean _updateToUserName = false; 035 private boolean _removeOldBean = false; 036 037 public ActionCreateBeansFromTable(String sys, String user) 038 throws BadUserNameException, BadSystemNameException { 039 super(sys, user); 040 _selectNamedBean.setOnlyDirectAddressingAllowed(); 041 } 042 043 @Override 044 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 045 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 046 String sysName = systemNames.get(getSystemName()); 047 String userName = userNames.get(getSystemName()); 048 if (sysName == null) sysName = manager.getAutoSystemName(); 049 ActionCreateBeansFromTable copy = new ActionCreateBeansFromTable(sysName, userName); 050 copy.setComment(getComment()); 051 copy.setOnlyCreatableTypes(_onlyCreatableTypes); 052 copy.setNamedBeanType(_namedBeanType); 053 _selectNamedBean.copy(copy._selectNamedBean); 054 copy.setTableRowOrColumn(_tableRowOrColumn); 055 copy.setRowOrColumnSystemName(_rowOrColumnSystemName); 056 copy.setRowOrColumnUserName(_rowOrColumnUserName); 057 copy.setIncludeCellsWithoutHeader(_includeCellsWithoutHeader); 058 copy.setMoveUserName(_moveUserName); 059 copy.setUpdateToUserName(_updateToUserName); 060 copy.setRemoveOldBean(_removeOldBean); 061 062 for (var entry : _namedBeansEntries) { 063 copy._namedBeansEntries.add( 064 new HashMap.SimpleEntry<>(entry.getKey(), entry.getValue())); 065 } 066 067 return manager.registerAction(copy); 068 } 069 070 /** 071 * Get whenever to show only types that can be created with this action. 072 * @return true if show only types that can be created, false otherwise 073 */ 074 public boolean isOnlyCreatableTypes() { 075 return _onlyCreatableTypes; 076 } 077 078 /** 079 * Set whenever to show only types that can be created with this action. 080 * @param onlyCreatableTypes true show only types that can be created, 081 * false otherwise 082 */ 083 public void setOnlyCreatableTypes(boolean onlyCreatableTypes) { 084 _onlyCreatableTypes = onlyCreatableTypes; 085 } 086 087 /** 088 * Get the type of the named beans 089 * @return the type of named beans 090 */ 091 public NamedBeanType getNamedBeanType() { 092 return _namedBeanType; 093 } 094 095 /** 096 * Set the type of the named beans 097 * @param namedBeanType the type of the named beans 098 */ 099 public void setNamedBeanType(@Nonnull NamedBeanType namedBeanType) { 100 if (namedBeanType == null) throw new RuntimeException("Daniel"); 101 _namedBeanType = namedBeanType; 102 } 103 104 public LogixNG_SelectNamedBean<NamedTable> getSelectNamedBean() { 105 return _selectNamedBean; 106 } 107 108 /** 109 * Get tableRowOrColumn. 110 * @return tableRowOrColumn 111 */ 112 public TableRowOrColumn getTableRowOrColumn() { 113 return _tableRowOrColumn; 114 } 115 116 /** 117 * Set tableRowOrColumn. 118 * @param tableRowOrColumn tableRowOrColumn 119 */ 120 public void setTableRowOrColumn(@Nonnull TableRowOrColumn tableRowOrColumn) { 121 _tableRowOrColumn = tableRowOrColumn; 122 } 123 124 /** 125 * Get name of row or column 126 * @return name of row or column 127 */ 128 public String getRowOrColumnSystemName() { 129 return _rowOrColumnSystemName; 130 } 131 132 /** 133 * Set name of row or column 134 * @param rowOrColumnName name of row or column 135 */ 136 public void setRowOrColumnSystemName(@Nonnull String rowOrColumnName) { 137 if (rowOrColumnName == null) throw new IllegalArgumentException("Row/column name is null"); 138 _rowOrColumnSystemName = rowOrColumnName; 139 } 140 141 /** 142 * Get name of row or column 143 * @return name of row or column 144 */ 145 public String getRowOrColumnUserName() { 146 return _rowOrColumnUserName; 147 } 148 149 /** 150 * Set name of row or column 151 * @param rowOrColumnName name of row or column 152 */ 153 public void setRowOrColumnUserName(@Nonnull String rowOrColumnName) { 154 if (rowOrColumnName == null) throw new IllegalArgumentException("Row/column name is null"); 155 _rowOrColumnUserName = rowOrColumnName; 156 } 157 158 /** 159 * Get whenever to include cells that doesn't have a header. 160 * Cells without headers can be used to use some cells in the table 161 * as comments. 162 * @return true if include cells that doesn't have a header, false otherwise 163 */ 164 public boolean isIncludeCellsWithoutHeader() { 165 return _includeCellsWithoutHeader; 166 } 167 168 /** 169 * Set whenever to include cells that doesn't have a header. 170 * Cells without headers can be used to use some cells in the table 171 * as comments. 172 * @param includeCellsWithoutHeader true if include rows/columns that 173 * doesn't have a header, false otherwise 174 */ 175 public void setIncludeCellsWithoutHeader(boolean includeCellsWithoutHeader) { 176 _includeCellsWithoutHeader = includeCellsWithoutHeader; 177 } 178 179 /** 180 * Get whenever to move the user name to the new bean. 181 * @return true if username should be moved, false otherwise 182 */ 183 public boolean isMoveUserName() { 184 return _moveUserName; 185 } 186 187 /** 188 * Set whenever to move the user name to the new bean. 189 * @param isMoveUserName true if username should be moved, false otherwise 190 */ 191 public void setMoveUserName(boolean isMoveUserName) { 192 _moveUserName = isMoveUserName; 193 } 194 195 /** 196 * Get whenever to use the user name for beans that already uses the system name. 197 * @return true if update beans to use user name, false otherwise 198 */ 199 public boolean isUpdateToUserName() { 200 return _updateToUserName; 201 } 202 203 /** 204 * Set whenever to use the user name for beans that already uses the system name. 205 * @param updateToUserName true if update beans to use user name, false otherwise 206 */ 207 public void setUpdateToUserName(boolean updateToUserName) { 208 _updateToUserName = updateToUserName; 209 } 210 211 /** 212 * Get whenever to remove the old bean. 213 * @return true if remove old bean, false otherwise 214 */ 215 public boolean isRemoveOldBean() { 216 return _removeOldBean; 217 } 218 219 /** 220 * Set whenever to remove the old bean. 221 * @param removeOldBean true if remove old bean, false otherwise 222 */ 223 public void setRemoveOldBean(boolean removeOldBean) { 224 _removeOldBean = removeOldBean; 225 } 226 227 /** {@inheritDoc} */ 228 @Override 229 public Category getCategory() { 230 return Category.OTHER; 231 } 232 233 private List<BeanName> getItems() { 234 List<BeanName> items = new ArrayList<>(); 235 236 if (_selectNamedBean.getNamedBean() == null) { 237 log.error("No table name is given"); 238 return items; // The list is empty 239 } 240 if (_rowOrColumnSystemName.isEmpty()) { 241 log.error("rowOrColumnSystemName is empty string"); 242 return items; // The list is empty 243 } 244 245 NamedTable table = _selectNamedBean.getBean(); 246 247 if (_tableRowOrColumn == TableRowOrColumn.Row) { 248 int systemNameRow = table.getRowNumber(_rowOrColumnSystemName); 249 int userNameRow = table.getRowNumber(_rowOrColumnUserName); 250 for (int column=1; column <= table.numColumns(); column++) { 251 // If the header is null or empty, treat the row as a comment 252 // unless _includeRowColumnWithoutHeader is true 253 Object header = table.getCell(0, column); 254// System.out.format("Row header: %s%n", header); 255 if (_includeCellsWithoutHeader 256 || ((header != null) && (!header.toString().isEmpty()))) { 257 Object systemNameCell = table.getCell(systemNameRow, column); 258 Object userNameCell = table.getCell(userNameRow, column); 259 if (systemNameCell != null && !systemNameCell.toString().isBlank()) { 260 if (userNameCell != null && !userNameCell.toString().isBlank()) { 261 items.add(new BeanName(systemNameCell.toString(), userNameCell.toString())); 262 } else { 263 items.add(new BeanName(systemNameCell.toString(), null)); 264 } 265 } 266 } 267 } 268 } else { 269 int systemNameColumn = table.getColumnNumber(_rowOrColumnSystemName); 270 int userNameColumn = table.getColumnNumber(_rowOrColumnUserName); 271 for (int row=1; row <= table.numRows(); row++) { 272 // If the header is null or empty, treat the row as a comment 273 // unless _includeRowColumnWithoutHeader is true 274 Object header = table.getCell(row, 0); 275// System.out.format("Column header: %s%n", header); 276 if (_includeCellsWithoutHeader 277 || ((header != null) && (!header.toString().isEmpty()))) { 278 Object systemNameCell = table.getCell(row, systemNameColumn); 279 Object userNameCell = table.getCell(row, userNameColumn); 280 if (systemNameCell != null && !systemNameCell.toString().isBlank()) { 281 if (userNameCell != null && !userNameCell.toString().isBlank()) { 282 items.add(new BeanName(systemNameCell.toString(), userNameCell.toString())); 283 } else { 284 items.add(new BeanName(systemNameCell.toString(), null)); 285 } 286 } 287 } 288 } 289 } 290 return items; 291 } 292 293 private void moveUserName( 294 NamedBean oldNameBean, 295 NamedBean newNameBean, 296 String userName) 297 throws JmriException { 298 299 NamedBeanHandleManager nbMan = InstanceManager.getDefault(NamedBeanHandleManager.class); 300 301 if (nbMan.inUse(oldNameBean.getSystemName(), oldNameBean)) { 302 if (_updateToUserName) { 303 nbMan.updateBeanFromSystemToUser(oldNameBean); 304 } 305 } 306 307 oldNameBean.setUserName(null); 308 newNameBean.setUserName(userName); 309 nbMan.moveBean(oldNameBean, newNameBean, userName); 310 } 311 312 /** {@inheritDoc} */ 313 @Override 314 public void execute() throws JmriException { 315 List<BeanName> items = getItems(); 316 for (BeanName beanName : items) { 317 NamedBean sysBean = _namedBeanType.getManager().getBySystemName(beanName._systemName); 318 NamedBean userBean = null; 319 if (beanName._userName != null && !beanName._userName.isBlank()) { 320 userBean = _namedBeanType.getManager().getByUserName(beanName._userName); 321 } 322 323 // Create new bean if it doesn't exists 324 if (sysBean == null) { 325 if (_namedBeanType.getCreateBean() == null) { 326 throw new JmriException(Bundle.getMessage( 327 "ActionCreateBeansFromTable_Exception_CreateBeanNotSupported", 328 _namedBeanType.getName(true))); 329 } 330 331 String userName = userBean != null ? null : beanName._userName; 332 try { 333 sysBean = _namedBeanType.getCreateBean().createBean(beanName._systemName, userName); 334 } catch (IllegalArgumentException e) { 335 throw new JmriException(Bundle.getMessage( 336 "ActionCreateBeansFromTable_Exception_CantCreateBean2", 337 beanName._systemName, e.getLocalizedMessage())); 338 } 339 if (sysBean == null) { 340 throw new JmriException(Bundle.getMessage( 341 "ActionCreateBeansFromTable_Exception_CantCreateBean", 342 beanName._systemName)); 343 } 344 } 345 346 if (userBean == null || sysBean == userBean) continue; 347 348 if (!_moveUserName) { 349 throw new JmriException(Bundle.getMessage("ActionCreateBeansFromTable_Exception_CantMoveUserName")); 350 } 351 352 moveUserName(userBean, sysBean, beanName._userName); 353 354 // Remove old bean if desired 355 if (_removeOldBean) { 356 try { 357 _namedBeanType.getDeleteBean().deleteBean(userBean, "CanDelete"); 358 } catch (java.beans.PropertyVetoException e) { 359 if (e.getPropertyChangeEvent().getPropertyName().equals("DoNotDelete")) { // NOI18N 360 throw new JmriException(String.format("Cannot delete bean: %s", e.getPropertyChangeEvent().getOldValue()), e); 361 } 362 } 363 try { 364 _namedBeanType.getDeleteBean().deleteBean(userBean, "DoDelete"); 365 } catch (java.beans.PropertyVetoException e) { 366 throw new JmriException(String.format("Cannot delete bean: %s", e.getPropertyChangeEvent().getOldValue()), e); 367 } 368 } 369 } 370 } 371 372 @Override 373 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 374 throw new UnsupportedOperationException("Not supported."); 375 } 376 377 @Override 378 public int getChildCount() { 379 return 0; 380 } 381 382 @Override 383 public String getShortDescription(Locale locale) { 384 return Bundle.getMessage(locale, "ActionCreateBeansFromTable_Short"); 385 } 386 387 @Override 388 public String getLongDescription(Locale locale) { 389 String tableName = _selectNamedBean.getDescription(locale); 390 String includeCellsWithoutHeaderStr = _includeCellsWithoutHeader 391 ? Bundle.getMessage(locale, "ActionCreateBeansFromTable_FlagStr", 392 Bundle.getMessage(locale, "ActionCreateBeansFromTable_IncludeCellsWithoutHeader")) 393 : ""; 394 String includeMoveUserNameStr = _moveUserName 395 ? Bundle.getMessage(locale, "ActionCreateBeansFromTable_FlagStr", 396 Bundle.getMessage(locale, "ActionCreateBeansFromTable_MoveUserName")) 397 : ""; 398 String updateToUserNameStr = _updateToUserName 399 ? Bundle.getMessage(locale, "ActionCreateBeansFromTable_FlagStr", 400 Bundle.getMessage(locale, "ActionCreateBeansFromTable_UpdateToUserName")) 401 : ""; 402 String includeRemoveOldBeanStr = _removeOldBean 403 ? Bundle.getMessage(locale, "ActionCreateBeansFromTable_FlagStr", 404 Bundle.getMessage(locale, "ActionCreateBeansFromTable_RemoveOldBean")) 405 : ""; 406 407 return Bundle.getMessage(locale, "ActionCreateBeansFromTable_Long", 408 _namedBeanType.getName(true).toLowerCase(), 409 tableName, 410 _tableRowOrColumn.getOpposite().toStringLowerCase(), 411 _tableRowOrColumn.toStringLowerCase(), 412 _rowOrColumnSystemName, 413 _rowOrColumnUserName, 414 includeCellsWithoutHeaderStr, 415 includeMoveUserNameStr, 416 updateToUserNameStr, 417 includeRemoveOldBeanStr); 418 } 419 420 /** {@inheritDoc} */ 421 @Override 422 public void setup() { 423 // Do nothing 424 } 425 426 /** {@inheritDoc} */ 427 @Override 428 public void registerListenersForThisClass() { 429 if (_listenersAreRegistered) return; 430 431 _selectNamedBean.registerListeners(); 432 _listenersAreRegistered = true; 433 } 434 435 /** {@inheritDoc} */ 436 @Override 437 public void unregisterListenersForThisClass() { 438 if (!_listenersAreRegistered) return; 439 440 _selectNamedBean.unregisterListeners(); 441 _listenersAreRegistered = false; 442 } 443 444 /** {@inheritDoc} */ 445 @Override 446 public void propertyChange(PropertyChangeEvent evt) { 447 getConditionalNG().execute(); 448 } 449 450 /** {@inheritDoc} */ 451 @Override 452 public void disposeMe() { 453 } 454 455 456 /** {@inheritDoc} */ 457 @Override 458 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 459 log.debug("getUsageReport :: ActionCreateBeansFromTable: bean = {}, report = {}", cdl, report); 460 if (_selectNamedBean.getBean() != null) { 461 if (bean.equals(_selectNamedBean.getBean())) { 462 report.add(new NamedBeanUsageReport("LogixNGAction", cdl, getLongDescription())); 463 } 464 } 465 } 466 467 468 private static class BeanName { 469 final String _systemName; 470 final String _userName; 471 472 BeanName(String systemName, String userName) { 473 _systemName = systemName; 474 _userName = userName; 475 } 476 } 477 478 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionCreateBeansFromTable.class); 479 480}