001package jmri.jmrit.logixng.implementation; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.beans.*; 006import java.io.File; 007import java.io.IOException; 008import java.io.PrintWriter; 009import java.text.DecimalFormat; 010import java.util.Locale; 011 012import javax.annotation.*; 013 014import jmri.*; 015import jmri.jmrit.logixng.*; 016import jmri.managers.AbstractManager; 017import jmri.util.*; 018 019/** 020 * Class providing the basic logic of the NamedTable_Manager interface. 021 * 022 * @author Dave Duchamp Copyright (C) 2007 023 * @author Daniel Bergqvist Copyright (C) 2020 024 */ 025public class DefaultNamedTableManager extends AbstractManager<NamedTable> 026 implements NamedTableManager { 027 028 DecimalFormat paddedNumber = new DecimalFormat("0000"); 029 030 031 /** 032 * {@inheritDoc} 033 */ 034 @Override 035 public int getXMLOrder() { 036 return LOGIXNG_TABLES; 037 } 038 039 /** 040 * {@inheritDoc} 041 */ 042 @Override 043 public char typeLetter() { 044 return 'Q'; 045 } 046 047 /** 048 * {@inheritDoc} 049 */ 050 @Override 051 public NameValidity validSystemNameFormat(String systemName) { 052 return LogixNG_Manager.validSystemNameFormat( 053 getSubSystemNamePrefix(), systemName); 054// if (systemName.matches(getSubSystemNamePrefix()+"(:AUTO:)?\\d+")) { 055// return NameValidity.VALID; 056// } else { 057// return NameValidity.INVALID; 058// } 059 } 060 061 062 /** 063 * {@inheritDoc} 064 */ 065 @Override 066 public NamedTable newCSVTable(String systemName, String userName, String fileName) 067 throws IllegalArgumentException { 068 return newCSVTable(systemName, userName, fileName, Table.CsvType.TABBED); 069 } 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 public NamedTable newCSVTable(String systemName, String userName, String fileName, Table.CsvType csvType) 076 throws IllegalArgumentException { 077 078 // Check that NamedTable does not already exist 079 NamedTable x; 080 if (userName != null && !userName.equals("")) { 081 x = getByUserName(userName); 082 if (x != null) { 083 return null; 084 } 085 } 086 x = getBySystemName(systemName); 087 if (x != null) { 088 return null; 089 } 090 // Check if system name is valid 091 if (this.validSystemNameFormat(systemName) != NameValidity.VALID) { 092 throw new IllegalArgumentException("SystemName " + systemName + " is not in the correct format"); 093 } 094 try { 095 log.debug("about to load file {}", fileName ); 096 // NamedTable does not exist, create a new NamedTable 097 x = AbstractNamedTable.loadTableFromCSV_File(systemName, userName, fileName, true, csvType); 098 } catch (IOException ex) { 099// Exceptions.printStackTrace(ex); 100 log.error("Cannot load table due to I/O error", ex); 101 return null; 102 } 103 // save in the maps 104 register(x); 105 106 // Keep track of the last created auto system name 107 updateAutoNumber(systemName); 108 109 return x; 110 } 111 112 /** 113 * {@inheritDoc} 114 */ 115 @Override 116 public NamedTable newInternalTable(String systemName, String userName, int numRows, int numColumns) 117 throws IllegalArgumentException { 118 119 // Check that NamedTable does not already exist 120 NamedTable x; 121 if (userName != null && !userName.equals("")) { 122 x = getByUserName(userName); 123 if (x != null) { 124 return null; 125 } 126 } 127 x = getBySystemName(systemName); 128 if (x != null) { 129 return null; 130 } 131 // Check if system name is valid 132 if (this.validSystemNameFormat(systemName) != NameValidity.VALID) { 133 throw new IllegalArgumentException("SystemName " + systemName + " is not in the correct format"); 134 } 135 // Table does not exist, create a new NamedTable 136 x = new DefaultInternalNamedTable(systemName, userName, numRows, numColumns); 137 // save in the maps 138 register(x); 139 140 // Keep track of the last created auto system name 141 updateAutoNumber(systemName); 142 143 return x; 144 } 145 146 /** 147 * {@inheritDoc} 148 */ 149 @Override 150 public AnonymousTable newAnonymousTable(int numRows, int numColumns) 151 throws IllegalArgumentException { 152 153 // Check that NamedTable does not already exist 154 // NamedTable does not exist, create a new NamedTable 155 return new DefaultAnonymousTable(numRows, numColumns); 156 } 157 158 /** 159 * {@inheritDoc} 160 */ 161 @Override 162 public NamedTable loadTableFromCSVData( 163 @Nonnull String sys, @CheckForNull String user, @Nonnull String text) 164 throws NamedBean.BadUserNameException, 165 NamedBean.BadSystemNameException, 166 IOException { 167 return AbstractNamedTable.loadTableFromCSV_Text(sys, user, text, true, Table.CsvType.TABBED); 168 } 169 170 /** 171 * {@inheritDoc} 172 */ 173 @Override 174 public NamedTable loadTableFromCSV( 175 @Nonnull String sys, @CheckForNull String user, 176 @Nonnull String fileName) 177 throws NamedBean.BadUserNameException, NamedBean.BadSystemNameException, IOException { 178 return AbstractNamedTable.loadTableFromCSV_File(sys, user, fileName, true, Table.CsvType.TABBED); 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public NamedTable loadTableFromCSV( 186 @Nonnull String sys, @CheckForNull String user, 187 @Nonnull File file) 188 throws NamedBean.BadUserNameException, NamedBean.BadSystemNameException, IOException { 189 return AbstractNamedTable.loadTableFromCSV_File(sys, user, file, true, Table.CsvType.TABBED); 190 } 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override 196 public NamedTable getNamedTable(String name) { 197 NamedTable x = getByUserName(name); 198 if (x != null) { 199 return x; 200 } 201 return getBySystemName(name); 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 @Override 208 public NamedTable getByUserName(String name) { 209 return _tuser.get(name); 210 } 211 212 /** 213 * {@inheritDoc} 214 */ 215 @Override 216 public NamedTable getBySystemName(String name) { 217 return _tsys.get(name); 218 } 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override 224 public String getBeanTypeHandled(boolean plural) { 225 return Bundle.getMessage(plural ? "BeanNameNamedTables" : "BeanNameNamedTable"); 226 } 227 228 /** 229 * {@inheritDoc} 230 */ 231 @Override 232 public void deleteNamedTable(NamedTable x) { 233 // delete the NamedTable 234 deregister(x); 235 x.dispose(); 236 } 237 238 /** {@inheritDoc} */ 239 @Override 240 public void printTree(PrintWriter writer, String indent) { 241 printTree(Locale.getDefault(), writer, indent); 242 } 243 244 /** {@inheritDoc} */ 245 @Override 246 public void printTree(Locale locale, PrintWriter writer, String indent) { 247 for (NamedTable namedTable : getNamedBeanSet()) { 248 if (namedTable instanceof DefaultCsvNamedTable) { 249 DefaultCsvNamedTable csvTable = (DefaultCsvNamedTable)namedTable; 250 writer.append(String.format( 251 "Named table: System name: %s, User name: %s, File name: %s, Num rows: %d, Num columns: %d", 252 csvTable.getSystemName(), csvTable.getUserName(), 253 csvTable.getFileName(), csvTable.numRows(), csvTable.numColumns())); 254 } else if (namedTable != null) { 255 writer.append(String.format( 256 "Named table: System name: %s, User name: %s, Num rows: %d, Num columns: %d", 257 namedTable.getSystemName(), namedTable.getUserName(), 258 namedTable.numRows(), namedTable.numColumns())); 259 } else { 260 throw new NullPointerException("namedTable is null"); 261 } 262 writer.println(); 263 } 264 writer.println(); 265 } 266 267 static volatile DefaultNamedTableManager _instance = null; 268 269 @InvokeOnGuiThread // this method is not thread safe 270 static public DefaultNamedTableManager instance() { 271 if (!ThreadingUtil.isGUIThread()) { 272 LoggingUtil.warnOnce(log, "instance() called on wrong thread"); 273 } 274 275 if (_instance == null) { 276 _instance = new DefaultNamedTableManager(); 277 } 278 return (_instance); 279 } 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override 285 public Class<NamedTable> getNamedBeanClass() { 286 return NamedTable.class; 287 } 288 289 /** 290 * Inform all registered listeners of a vetoable change.If the propertyName 291 * is "CanDelete" ALL listeners with an interest in the bean will throw an 292 * exception, which is recorded returned back to the invoking method, so 293 * that it can be presented back to the user.However if a listener decides 294 * that the bean can not be deleted then it should throw an exception with 295 * a property name of "DoNotDelete", this is thrown back up to the user and 296 * the delete process should be aborted. 297 * 298 * @param p The programmatic name of the property that is to be changed. 299 * "CanDelete" will inquire with all listeners if the item can 300 * be deleted. "DoDelete" tells the listener to delete the item. 301 * @param old The old value of the property. 302 * @throws java.beans.PropertyVetoException If the recipients wishes the 303 * delete to be aborted (see above) 304 */ 305 @OverridingMethodsMustInvokeSuper 306 public void fireVetoableChange(String p, Object old) throws PropertyVetoException { 307 PropertyChangeEvent evt = new PropertyChangeEvent(this, p, old, null); 308 for (VetoableChangeListener vc : vetoableChangeSupport.getVetoableChangeListeners()) { 309 vc.vetoableChange(evt); 310 } 311 } 312 313 /** {@inheritDoc} */ 314 @Override 315 @SuppressFBWarnings(value = "OVERRIDING_METHODS_MUST_INVOKE_SUPER", 316 justification = "Further investigation is needed to handle this correctly") 317 public final void deleteBean(@Nonnull NamedTable namedTable, @Nonnull String property) throws PropertyVetoException { 318 // throws PropertyVetoException if vetoed 319 fireVetoableChange(property, namedTable); 320 if (property.equals("DoDelete")) { // NOI18N 321 deregister(namedTable); 322 namedTable.dispose(); 323 } 324 } 325 326 327 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultNamedTableManager.class); 328 329}