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