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