001package jmri.jmrit.catalog;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004
005import java.util.Set;
006
007import javax.annotation.Nonnull;
008import javax.annotation.CheckForNull;
009
010import jmri.*;
011import jmri.implementation.AbstractInstanceInitializer;
012import jmri.implementation.swing.SwingShutDownTask;
013import jmri.jmrit.catalog.configurexml.DefaultCatalogTreeManagerXml;
014import jmri.jmrix.internal.InternalSystemConnectionMemo;
015import jmri.managers.AbstractManager;
016
017import org.openide.util.lookup.ServiceProvider;
018import org.slf4j.Logger;
019import org.slf4j.LoggerFactory;
020
021/**
022 * Provide the concrete implementation for the Internal CatalogTree Manager.
023 * <p>
024 * Control of the systemName is internal so the more casual approach like that of
025 * SignalHeadManager is used rather than the ProxyManager style.
026 *
027 * @author Pete Cressman Copyright (C) 2009
028 */
029public class DefaultCatalogTreeManager extends AbstractManager<CatalogTree> implements CatalogTreeManager {
030
031    private boolean _indexChanged = false;
032    private boolean _indexLoaded = false;
033    private ShutDownTask _shutDownTask;
034
035    public DefaultCatalogTreeManager() {
036        super(InstanceManager.getDefault(InternalSystemConnectionMemo.class));
037    }
038
039    /**
040     * Override parent method to not register this object to be stored
041     * automatically as part of the general storage mechanism.
042     */
043    @Override
044    @SuppressFBWarnings(value = "OVERRIDING_METHODS_MUST_INVOKE_SUPER",
045            justification = "This method intentionally doesn't register anything")
046    protected void registerSelf() {
047        log.debug("not registering");
048    }
049
050    @Override
051    public int getXMLOrder() {
052        return 65400;
053    }
054
055    /**
056     * Bogus typeLetter
057     */
058    @Override
059    public char typeLetter() {
060        return '0';
061    }
062
063    @CheckForNull
064    @Override
065    public CatalogTree getCatalogTree(@Nonnull String name) {
066        CatalogTree t = getByUserName(name);
067        return (t != null ? t : getBySystemName(name));
068    }
069
070    @CheckForNull
071    @Override
072    public CatalogTree getBySystemName(@Nonnull String key) {
073        if (log.isDebugEnabled()) {
074            log.debug("getBySystemName: systemName= {}", key);
075            CatalogTree tree = _tsys.get(key);
076            if (tree != null) {
077                CatalogTreeNode root = tree.getRoot();
078                log.debug("root= {}, has {} children", root,root.getChildCount());
079            }
080        }
081        return _tsys.get(key);
082    }
083
084    @CheckForNull
085    @Override
086    public CatalogTree getByUserName(@Nonnull String key) {
087        return _tuser.get(key);
088    }
089
090    /**
091     * Provide CatalogTree by UserName, then SystemName, then creates if not found.
092     * {@inheritDoc}
093     */
094    @Override
095    @Nonnull
096    public CatalogTree newCatalogTree(@Nonnull String systemName, String userName) throws IllegalArgumentException {
097        log.debug("new CatalogTree: systemName= {}, userName= {}", systemName, userName);
098        if (systemName.isEmpty()) {
099            throw new IllegalArgumentException("Empty systemName!");
100        }
101        // return existing if there is one
102        CatalogTree t;
103        if (userName != null) {
104            t = getByUserName(userName);
105            if (t != null) {
106                if (getBySystemName(systemName) != t) {
107                    log.error("inconsistent user ({}) and system name ({}) results; userName related to ({})",
108                            userName, systemName, t.getSystemName());
109                }
110                return t;
111            }
112        }
113        t = getBySystemName(systemName);
114        if (t != null) {
115            if ((t.getUserName() == null) && (userName != null)) {
116                t.setUserName(userName);
117            } else if (userName != null) {
118                log.warn("Found memory via system name ({}) with non-null userName ({})",
119                        systemName, userName);
120            }
121            return t;
122        }
123        // doesn't exist, make a new one
124        t = createNewCatalogTree(systemName, userName);
125        // save in the maps
126        register(t);
127        return t;
128    }
129
130    /**
131     * Create a CatalogTree.
132     * <p>
133     * Naming convention is:
134     * <pre>
135     *   IF... - filter for image files from the file system
136     *   SF... - filter for sound files from the file system
137     *   TF... - filter for script files from the file system
138     *   NF... - no filter for files from the file system
139     *   IX... - index for image files stored in XML config file
140     *   SX... - index for sound files stored in XML config file
141     *   TX... - index for script files stored in XML config file
142     *   NX... - index for files stored in XML config file
143     * </pre>
144     *
145     * @param systemName system name for catalog tree, never null/empty
146     * @param userName   user name for catalog tree
147     * @return the new catalog tree or
148     * @throws IllegalArgumentException if unable to create
149     */
150    @Nonnull
151    protected CatalogTree createNewCatalogTree(@Nonnull String systemName, String userName) throws IllegalArgumentException {
152        if (systemName.isEmpty()) {
153            throw new IllegalArgumentException("Empty systemName!");
154        }
155        if (userName == null || userName.isEmpty()) {
156            throw new IllegalArgumentException("Null or empty userName!");
157        }
158        if (systemName.charAt(1) == CatalogTree.XML) {
159            switch (systemName.charAt(0)) {
160                case CatalogTree.IMAGE:
161                case CatalogTree.SOUND:
162                case CatalogTree.SCRIPT:
163                case CatalogTree.NOFILTER:
164                    return new CatalogTreeIndex(systemName, userName);
165                default:
166                    log.error("Bad systemName: {} (userName= {})", systemName, userName);
167            }
168        } else if (systemName.charAt(1) == CatalogTree.FILESYS) {
169            CatalogTreeFS catTree;
170            switch (systemName.charAt(0)) {
171                case CatalogTree.IMAGE:
172                    catTree = new CatalogTreeFS(systemName, userName);
173                    catTree.setFilter(IMAGE_FILTER);
174                    return catTree;
175                case CatalogTree.SOUND:
176                    catTree = new CatalogTreeFS(systemName, userName);
177                    catTree.setFilter(SOUND_FILTER);
178                    return catTree;
179                case CatalogTree.SCRIPT:
180                    catTree = new CatalogTreeFS(systemName, userName);
181                    catTree.setFilter(SCRIPT_FILTER);
182                    return catTree;
183                case CatalogTree.NOFILTER:
184                    return new CatalogTreeFS(systemName, userName);
185                default:
186                    log.error("Bad systemName: {} (userName= {})", systemName, userName);
187            }
188        }
189        throw new IllegalArgumentException("systemName.charAt not XML or FILESYS !");
190    }
191
192    @Override
193    @Nonnull
194    public String getBeanTypeHandled(boolean plural) {
195        return Bundle.getMessage(plural ? "BeanNameCatalogs" : "BeanNameCatalog");
196    }
197
198    /**
199     * {@inheritDoc}
200     */
201    @Override
202    public Class<CatalogTree> getNamedBeanClass() {
203        return CatalogTree.class;
204    }
205
206    /**
207     * {@inheritDoc}
208     */
209    @Override
210    public void storeImageIndex() {
211        jmri.jmrit.display.palette.ItemPalette.storeIcons();
212
213        log.debug("Start writing CatalogTree info");
214        try {
215            new DefaultCatalogTreeManagerXml().writeCatalogTrees();
216            indexChanged(false);
217        } catch (java.io.IOException ioe) {
218            log.error("Exception writing CatalogTrees: ", ioe);
219        }
220    }
221
222    /**
223     * Load the index file, one time per session.
224     */
225    @Override
226    public void loadImageIndex() {
227        if (!isIndexLoaded()) {
228            new DefaultCatalogTreeManagerXml().readCatalogTrees();
229            _indexLoaded = true;
230            log.debug("loadImageIndex: catalog file loaded");
231        }
232    }
233
234    /**
235     * {@inheritDoc}
236     */
237    @Override
238    public boolean isIndexChanged() {
239        return _indexChanged;
240    }
241
242    /**
243     * {@inheritDoc}
244     */
245    @Override
246    public boolean isIndexLoaded() {
247        return _indexLoaded;
248    }
249
250    /**
251     * {@inheritDoc}
252     */
253    @Override
254    public final synchronized void indexChanged(boolean changed) {
255        _indexChanged = changed;
256        ShutDownManager sdm = InstanceManager.getDefault(ShutDownManager.class);
257        if (changed) {
258            if (_shutDownTask == null) {
259                _shutDownTask = new SwingShutDownTask("PanelPro Save default icon check",
260                        Bundle.getMessage("IndexChanged"),
261                        Bundle.getMessage("SaveAndQuit"), null) {
262                    @Override
263                    public boolean checkPromptNeeded() {
264                        return !_indexChanged;
265                    }
266
267                    @Override
268                    public void didPrompt() {
269                        storeImageIndex();
270                    }
271                };
272                sdm.register(_shutDownTask);
273            }
274        } else if (_shutDownTask != null) {
275            sdm.deregister(_shutDownTask);
276            _shutDownTask = null;
277        }
278    }
279
280    private static final Logger log = LoggerFactory.getLogger(DefaultCatalogTreeManager.class);
281
282    @ServiceProvider(service = InstanceInitializer.class)
283    public static class Initializer extends AbstractInstanceInitializer {
284
285        @Override
286        @Nonnull
287        public <T> Object getDefault(Class<T> type) {
288            if (type.equals(CatalogTreeManager.class)) {
289                return new DefaultCatalogTreeManager();
290            }
291            return super.getDefault(type);
292        }
293
294        @Override
295        @Nonnull
296        public Set<Class<?>> getInitalizes() {
297            Set<Class<?>> set = super.getInitalizes();
298            set.add(CatalogTreeManager.class);
299            return set;
300        }
301
302    }
303
304}