001package jmri.jmrix.can.cbus.swing.modules;
002
003import java.util.*;
004
005import javax.annotation.CheckForNull;
006import javax.annotation.Nonnull;
007
008import jmri.jmrix.can.cbus.node.CbusNode;
009import jmri.jmrix.can.cbus.node.CbusNodeNVTableDataModel;
010import jmri.spi.JmriServiceProviderInterface;
011
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015
016/**
017 * Pane for configuring events in a CBUS module
018 * 
019 * Definition of objects to handle configuring a CBUS module.
020 *
021 * Implementing classes <em>must</em> be registered as service providers of this
022 * type to be recognized and usable.
023 * <p>
024 * General design documentation is available on the 
025 * <a href="http://jmri.org/help/en/html/doc/Technical/SystemStructure.shtml">Structure of External System Connections page</a>.
026 *
027 * @author Andrew Crosland Copyright (C) 2021
028 * @see java.util.ServiceLoader
029 */
030public abstract class CbusConfigPaneProvider extends jmri.jmrix.can.swing.CanPanel implements JmriServiceProviderInterface {
031
032    protected CbusConfigPaneProvider() {
033        super();
034    }
035    
036    /**
037     * Get the manufacturer name for the class
038     *
039     * @return the manufacturer
040     */
041    @Nonnull
042    abstract public String getModuleType();
043
044    /**
045     * Get descriptive name of NV
046     * 
047     * @param index of the NV
048     * @return the name as String. May be empty string if NV description is unknown
049     * or null if index is out of range
050     */
051    abstract public String getNVNameByIndex(int index);
052
053    protected AbstractEditNVPane _nVarEditFrame = null;
054    
055    /**
056     * Get the edit frame instance
057     * @return the edit frame
058     */
059    abstract public AbstractEditNVPane getEditNVFrameInstance();
060    
061    /**
062     * Create a new edit frame
063     * 
064     * @param editFrame the containing frame
065     * @param node the node to be edited
066     * @return the edit frame
067     */
068    abstract public AbstractEditNVPane getEditNVFrame(CbusNodeNVTableDataModel editFrame, CbusNode node);
069
070    /**
071     * Return string representation of the node
072     * 
073     * @return name of object
074     */
075    @Override
076    final public String toString() {
077        return getModuleType();
078    }
079    
080    /**
081     * Get a module provider from a module name
082     * 
083     * @param name of the module
084     * @return the module provider, null if not known
085     */
086    @CheckForNull
087    final static public CbusConfigPaneProvider getProviderByName(String name) {
088        loadInstances();
089        CbusConfigPaneProvider p = instanceMap.get(name);
090        return p;
091    }
092
093    /**
094     * Get a module provider from a CBUS node
095     * 
096     * @param node the node instance
097     * @return the module provider
098     */
099    final static public CbusConfigPaneProvider getProviderByNode(CbusNode node) {
100        loadInstances();
101        CbusConfigPaneProvider p = instanceMap.get(node.getName());
102        if (p != null) {
103            return p;
104        } else if (node.getResyncName() != null) {
105            // Get the saved name during a resync
106            p = instanceMap.get(node.getResyncName());
107            if (p != null) {
108                return p;
109            }
110        }
111        jmri.util.LoggingUtil.infoOnce(log,"node gets unknown provider: {}", node);
112        return new UnknownPaneProvider();
113    }
114
115    /**
116     * Get all available instances as an {@link Collections#unmodifiableMap}
117     * between the (localized) name and the pane. Note that this is a SortedMap in 
118     * name order.
119     * 
120     * @return all instance map sorted in name order.
121     */
122    final static public Map<String, CbusConfigPaneProvider> getInstancesMap() {
123        loadInstances();
124        return Collections.unmodifiableMap(instanceMap);
125    }
126
127    /**
128     * Get all available instances as an {@link Collections#unmodifiableCollection}
129     * between the (localized) name and the pane.
130     * 
131     * @return unmodifiable collection.
132     */
133    final static public Collection<CbusConfigPaneProvider> getInstancesCollection() {
134        loadInstances();
135        return Collections.unmodifiableCollection(instanceMap.values());
136    }
137
138    /**
139     * Load all the available instances. Note this only runs
140     * once; there's no reloading once the program is running.
141     */
142    final static public void loadInstances() {
143        if (instanceMap != null) return;
144
145        instanceMap = new TreeMap<>();  // sorted map, in string order on key
146
147        java.util.ServiceLoader.load(CbusConfigPaneProvider.class).forEach((pane) -> {
148            if (!instanceMap.containsKey(pane.getModuleType())) {
149                instanceMap.put(pane.getModuleType(), pane);
150            }
151        });
152
153    }
154
155    static volatile Map<String, CbusConfigPaneProvider> instanceMap = null;
156
157    private final static Logger log = LoggerFactory.getLogger(CbusConfigPaneProvider.class);
158}