001package jmri.managers;
002
003import java.time.LocalDateTime;
004import java.util.Arrays;
005import java.util.LinkedList;
006import java.util.List;
007import javax.annotation.Nonnull;
008
009import jmri.*;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013/**
014 * Implementation of a TurnoutManager that can serve as a proxy for multiple
015 * system-specific implementations.
016 *
017 * @author Bob Jacobsen Copyright (C) 2003, 2010
018 */
019public class ProxyTurnoutManager extends AbstractProvidingProxyManager<Turnout> implements TurnoutManager {
020
021    public ProxyTurnoutManager() {
022        super();
023    }
024
025    @Override
026    protected AbstractManager<Turnout> makeInternalManager() {
027        return jmri.InstanceManager.getDefault(jmri.jmrix.internal.InternalSystemConnectionMemo.class).getTurnoutManager();
028    }
029
030    /**
031     * {@inheritDoc}
032     */
033    @Override
034    public void addManager(@Nonnull Manager<Turnout> m) {
035        super.addManager(m);
036        InstanceManager.getDefault(TurnoutOperationManager.class).loadOperationTypes();
037    }
038
039    /**
040     * Locate via user name, then system name if needed.
041     *
042     * @return Null if nothing by that name exists
043     */
044    @Override
045    public Turnout getTurnout(@Nonnull String name) {
046        return super.getNamedBean(name);
047    }
048
049    /**
050     * {@inheritDoc}
051     */
052    @Override
053    @Nonnull
054    protected Turnout makeBean(Manager<Turnout> manager, String systemName, String userName) throws IllegalArgumentException {
055        return ((TurnoutManager) manager).newTurnout(systemName, userName);
056    }
057
058    /**
059     * {@inheritDoc}
060     */
061    @Override
062    @Nonnull
063    public Turnout provideTurnout(@Nonnull String name) throws IllegalArgumentException {
064        return super.provideNamedBean(name);
065    }
066
067
068    /** {@inheritDoc} */
069    @Override
070    @Nonnull
071    public Turnout provide(@Nonnull String name) throws IllegalArgumentException { return provideTurnout(name); }
072
073    /**
074     * Get an instance with the specified system and user names. Note that
075     * two calls with the same arguments will get the same instance; there is
076     * only one Sensor object representing a given physical turnout and
077     * therefore only one with a specific system or user name.
078     * <p>
079     * This will always return a valid object reference for a valid request; a
080     * new object will be created if necessary. In that case:
081     * <ul>
082     * <li>If a null reference is given for user name, no user name will be
083     * associated with the Turnout object created; a valid system name must be
084     * provided
085     * <li>If a null reference is given for the system name, a system name will
086     * _somehow_ be inferred from the user name. How this is done is system
087     * specific. Note: a future extension of this interface will add an
088     * exception to signal that this was not possible.
089     * <li>If both names are provided, the system name defines the hardware
090     * access of the desired turnout, and the user address is associated with
091     * it.
092     * </ul>
093     * Note that it is possible to make an inconsistent request if both
094     * addresses are provided, but the given values are associated with
095     * different objects. This is a problem, and we don't have a good solution
096     * except to issue warnings. This will mostly happen if you're creating
097     * Sensors when you should be looking them up.
098     *
099     * @return requested Turnout object (never null)
100     */
101    @Override
102    @Nonnull
103    public Turnout newTurnout(@Nonnull String systemName, String userName) throws IllegalArgumentException {
104        return newNamedBean(systemName, userName);
105    }
106
107    /**
108     * Get text to be used for the Turnout.CLOSED state in user communication.
109     * Allows text other than "CLOSED" to be use with certain hardware system to
110     * represent the Turnout.CLOSED state. Defaults to the primary manager. This
111     * means that the primary manager sets the terminology used. Note: the
112     * primary manager need not override the method in AbstractTurnoutManager if
113     * "CLOSED" is the desired terminology.
114     */
115    @Override
116    @Nonnull
117    public String getClosedText() {
118        return ((TurnoutManager) getDefaultManager()).getClosedText();
119    }
120
121    /**
122     * Get text to be used for the Turnout.THROWN state in user communication.
123     * Allows text other than "THROWN" to be use with certain hardware system to
124     * represent the Turnout.THROWN state. Defaults to the primary manager. This
125     * means that the primary manager sets the terminology used. Note: the
126     * primary manager need not override the method in AbstractTurnoutManager if
127     * "THROWN" is the desired terminology.
128     */
129    @Override
130    @Nonnull
131    public String getThrownText() {
132        return ((TurnoutManager) getDefaultManager()).getThrownText();
133    }
134
135    /**
136     * Get from the user, the number of addressed bits used to control a
137     * turnout. Normally this is 1, and the default routine returns 1
138     * automatically. Turnout Managers for systems that can handle multiple
139     * control bits should override this method with one which asks the user to
140     * specify the number of control bits. If the user specifies more than one
141     * control bit, this method should check if the additional bits are
142     * available (not assigned to another object). If the bits are not
143     * available, this method should return 0 for number of control bits, after
144     * informing the user of the problem.
145     */
146    @Override
147    public int askNumControlBits(@Nonnull String systemName) {
148        return ((TurnoutManager) getManagerOrDefault(systemName)).askNumControlBits(systemName);
149    }
150
151    /**
152     * Get from the user, the type of output to be used bits to control a
153     * turnout. Normally this is 0 for 'steady state' control, and the default
154     * routine returns 0 automatically. Turnout Managers for systems that can
155     * handle pulsed control as well as steady state control should override
156     * this method with one which asks the user to specify the type of control
157     * to be used. The routine should return 0 for 'steady state' control, or n
158     * for 'pulsed' control, where n specifies the duration of the pulse
159     * (normally in seconds).
160     */
161    @Override
162    public int askControlType(@Nonnull String systemName) {
163        return ((TurnoutManager) getManagerOrDefault(systemName)).askControlType(systemName);
164    }
165
166    /**
167     * {@inheritDoc}
168     */
169    @Override
170    public boolean isControlTypeSupported(@Nonnull String systemName) {
171        return ((TurnoutManager) getManagerOrDefault(systemName)).isControlTypeSupported(systemName);
172    }
173
174    /**
175     * {@inheritDoc}
176     */
177    @Override
178    public boolean isNumControlBitsSupported(@Nonnull String systemName) {
179        return ((TurnoutManager) getManagerOrDefault(systemName)).isNumControlBitsSupported(systemName);
180    }
181
182    /** {@inheritDoc} */
183    @Override
184    @Nonnull
185    public String[] getValidOperationTypes() {
186        List<String> typeList = new LinkedList<>();
187        getManagerList().forEach(m -> typeList.addAll(Arrays.asList(((TurnoutManager) m).getValidOperationTypes())));
188        return TurnoutOperationManager.concatenateTypeLists(typeList.toArray(new String[0]));
189    }
190
191    /**
192     * {@inheritDoc}
193     */
194    @Override
195    public boolean allowMultipleAdditions(@Nonnull String systemName) {
196        return ((TurnoutManager) getManagerOrDefault(systemName)).allowMultipleAdditions(systemName);
197    }
198
199    @SuppressWarnings("deprecation") // user warned by actual manager class
200    @Override
201    public String getNextValidAddress(@Nonnull String curAddress, @Nonnull String prefix) throws jmri.JmriException {
202        return getNextValidAddress(curAddress, prefix, typeLetter());
203    }
204    
205    @Override
206    public String getNextValidAddress(@Nonnull String curAddress, @Nonnull String prefix, boolean ignoreInitialExisting) throws jmri.JmriException {
207        return getNextValidAddress(curAddress, prefix, ignoreInitialExisting, typeLetter());
208    }
209
210    /**
211     * {@inheritDoc}
212     */
213    @Override
214    public void setDefaultClosedSpeed(@Nonnull String speed) throws jmri.JmriException {
215        for (Manager<Turnout> m : getManagerList()) {
216            try {
217                ((TurnoutManager) m).setDefaultClosedSpeed(speed);
218            } catch (jmri.JmriException ex) {
219                log.error(ex.toString());
220                throw ex;
221            }
222        }
223    }
224
225    /**
226     * {@inheritDoc}
227     */
228    @Override
229    public void setDefaultThrownSpeed(@Nonnull String speed) throws jmri.JmriException {
230        for (Manager<Turnout> m : getManagerList()) {
231            try {
232                ((TurnoutManager) m).setDefaultThrownSpeed(speed);
233            } catch (jmri.JmriException ex) {
234                log.error(ex.toString());
235                throw ex;
236            }
237        }
238    }
239
240    /**
241     * {@inheritDoc}
242     */
243    @Override
244    public String getDefaultThrownSpeed() {
245        return ((TurnoutManager) getDefaultManager()).getDefaultThrownSpeed();
246    }
247
248    /**
249     * {@inheritDoc}
250     */
251    @Override
252    public String getDefaultClosedSpeed() {
253        return ((TurnoutManager) getDefaultManager()).getDefaultClosedSpeed();
254    }
255
256    /** {@inheritDoc}
257     * @return outputInterval from default TurnoutManager
258     */
259    @Override
260    public int getOutputInterval() {
261        return ((TurnoutManager) getDefaultManager()).getOutputInterval();
262    }
263
264    /**
265     * {@inheritDoc}
266     * This method is only used in jmri.jmrix.internal.InternalTurnoutManagerTest and should not be
267     * used in actual code, as it can overwrite individual per connection values set by the user.
268     */
269    @Override
270    public void setOutputInterval(int newInterval) {
271        log.debug("setOutputInterval called in ProxyTurnoutManager");
272        // only intended for testing; do not set interval via ProxyTurnoutManager in actual code
273        for (Manager<Turnout> manager : getManagerList()) {
274            ((TurnoutManager) manager).setOutputInterval(newInterval);
275        }
276    }
277
278    /**
279     * {@inheritDoc}
280     * @return end time of latest OutputInterval as LocalDateTime from default TurnoutManager
281     */
282    @Nonnull
283    @Override
284    public LocalDateTime outputIntervalEnds() {
285        log.debug("outputIntervalEnds called in ProxyTurnoutManager");
286        return ((TurnoutManager) getDefaultManager()).outputIntervalEnds();
287    }
288
289    /**
290     * {@inheritDoc}
291     */
292    @Override
293    public int getXMLOrder() {
294        return jmri.Manager.TURNOUTS;
295    }
296
297    /**
298     * {@inheritDoc}
299     */
300    @Override
301    @Nonnull
302    public String getBeanTypeHandled(boolean plural) {
303        return Bundle.getMessage(plural ? "BeanNameTurnouts" : "BeanNameTurnout");
304    }
305
306    /**
307     * {@inheritDoc}
308     */
309    @Override
310    public Class<Turnout> getNamedBeanClass() {
311        return Turnout.class;
312    }
313
314    // initialize logging
315    private final static Logger log = LoggerFactory.getLogger(ProxyTurnoutManager.class);
316
317}