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