001package jmri.jmrit.beantable.signalmast; 002 003 004import java.util.*; 005 006import javax.annotation.Nonnull; 007import javax.swing.*; 008 009import jmri.*; 010import jmri.spi.JmriServiceProviderInterface; 011 012/** 013 * Definition of JPanel used to configure a specific SignalMast type. 014 * 015 * Implementing classes <em>must</em> be registered as service providers of this 016 * type to be recognized and usable. 017 * <p> 018 * General design documentation is available on the 019 * <a href="http://jmri.org/help/en/html/doc/Technical/SystemStructure.shtml">Structure of 020 * External System Connections page</a>. 021 * 022 * The general sequence is: 023 * <ul> 024 * <li>Find one or more object of this type that have {@link SignalMastAddPaneProvider#isAvailable} true. 025 * <li>Invoke {@link #setAspectNames} from the selected signal system 026 * <li>If you're showing a mast that exists, invoke {@link #setMast} to load the contents 027 * <li>To eventually create or update a mast from the entered data, invoke {@link #createMast} 028 * </ul> 029 * 030 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2018 031 * @see java.util.ServiceLoader 032 * @see AddSignalMastPanel 033 * @since 4.11.3 034 */ 035public abstract class SignalMastAddPane extends JPanel implements JmriServiceProviderInterface { 036 037 /** 038 * Provide a new list of aspects in the signal system. 039 * Must be done at startup before the pane is shown. 040 * May be done later, to update to a newly selected system. 041 * @param map the signal appearance map. 042 * @param sigSystem the signal system. 043 */ 044 abstract public void setAspectNames(@Nonnull SignalAppearanceMap map, 045 @Nonnull SignalSystem sigSystem); 046 047 /** 048 * Can this pane edit a specific mast object, i.e. an object of its type? 049 * 050 * @param mast the SignalMast to possibly display 051 * @return true if this pane can handle that mast type; false if can't 052 */ 053 abstract public boolean canHandleMast(@Nonnull SignalMast mast); 054 055 /** 056 * Load this pane with information from a mast. 057 * Do not invoke this if {@link #canHandleMast(SignalMast)} on that mast returns false. 058 * 059 * @param mast the SignalMast to display or null to reset a previous setting 060 */ 061 abstract public void setMast(SignalMast mast); 062 063 /** 064 * Called to either "create and register" a new, or "update" an existing mast from the given information. 065 * 066 * @param sigsysname the name of the signal system in use 067 * @param mastname the mast type name 068 * @param username user name value 069 * @return false if the operation failed, in which case the user should have already been notified 070 */ 071 abstract public boolean createMast(@Nonnull String sigsysname, @Nonnull String mastname, @Nonnull String username); 072 073 /** 074 * @return human-preferred name for type of signal mast, in local language 075 */ 076 @Nonnull abstract public String getPaneName(); 077 078 final protected static int NOTIONAL_ASPECT_COUNT = 12; // size of maps, not critical 079 080 static public abstract class SignalMastAddPaneProvider implements JmriServiceProviderInterface { 081 /** 082 * Is this pane available, given the current configuration of the program? 083 * In other words, are all necessary managers and other objects present? 084 * @return always true. 085 */ 086 public boolean isAvailable() { return true; } 087 088 /** 089 * @return Human-prefered name for type of signal mast, in local language 090 */ 091 @Nonnull abstract public String getPaneName(); 092 093 /** 094 * @return A new instance of this SignalMastAddPane class 095 */ 096 @Nonnull abstract public SignalMastAddPane getNewPane(); 097 098 /** 099 * Get all available instances as an {@link Collections#unmodifiableMap} 100 * between the (localized) name and the pane. Note that this is a SortedMap in 101 * name order. 102 * @return all instance map sorted in name order. 103 */ 104 final static public Map<String, SignalMastAddPaneProvider> getInstancesMap() { 105 if (instanceMap == null) loadInstances(); 106 return Collections.unmodifiableMap(instanceMap); 107 } 108 109 /** 110 * Get all available instances as an {@link Collections#unmodifiableCollection} 111 * between the (localized) name and the pane. 112 * @return unmodifiable collection. 113 */ 114 final static public Collection<SignalMastAddPaneProvider> getInstancesCollection() { 115 if (instanceMap == null) loadInstances(); 116 return Collections.unmodifiableCollection(instanceMap.values()); 117 } 118 119 /** 120 * Load all the available instances. Note this only runs 121 * once; there's no reloading once the program is running. 122 */ 123 final static public void loadInstances() { 124 if (instanceMap != null) return; 125 126 instanceMap = new TreeMap<>(); // sorted map, in string order on key 127 128 java.util.ServiceLoader.load(SignalMastAddPaneProvider.class).forEach((pane) -> { 129 if (pane.isAvailable()) { 130 instanceMap.put(pane.getPaneName(), pane); 131 } 132 }); 133 134 } 135 136 static volatile Map<String, SignalMastAddPaneProvider> instanceMap = null; 137 } 138 139}