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