001package jmri.managers;
002
003import java.lang.reflect.InvocationTargetException;
004import java.util.ArrayList;
005import java.util.List;
006import java.util.Objects;
007
008import javax.annotation.CheckForNull;
009import javax.annotation.Nonnull;
010
011import jmri.*;
012import jmri.implementation.*;
013import jmri.jmrix.internal.InternalSystemConnectionMemo;
014import jmri.util.StringUtil;
015
016/**
017 * Default implementation of a SignalMastManager.
018 * <p>
019 * Note that this does not enforce any particular system naming convention at
020 * the present time. They're just names...
021 *
022 * @author Bob Jacobsen Copyright (C) 2009, 2020
023 */
024public class DefaultSignalMastManager extends AbstractManager<SignalMast>
025        implements SignalMastManager {
026
027    public DefaultSignalMastManager(InternalSystemConnectionMemo memo) {
028        super(memo);
029        repeaterList = new ArrayList<>();
030        addListeners();
031    }
032
033    final void addListeners(){
034        InstanceManager.getDefault(SignalHeadManager.class).addVetoableChangeListener(this);
035        InstanceManager.getDefault(TurnoutManager.class).addVetoableChangeListener(this);
036    }
037
038    /** {@inheritDoc} */
039    @Override
040    public int getXMLOrder() {
041        return Manager.SIGNALMASTS;
042    }
043
044    /** {@inheritDoc} */
045    @Override
046    public char typeLetter() {
047        return 'F';
048    }
049
050    /**
051     * {@inheritDoc}
052     * Searches by UserName then SystemName.
053     */
054    @Override
055    @CheckForNull
056    public SignalMast getSignalMast(@Nonnull String name) {
057        if (Objects.isNull(name) || name.length() == 0) {
058            return null;
059        }
060        SignalMast t = getByUserName(name);
061        return ( t != null ? t : getBySystemName(name));
062    }
063
064    /** {@inheritDoc} */
065    @Override
066    @Nonnull
067    public SignalMast provideSignalMast(@Nonnull String prefix, // nominally IF$shsm
068                                        @Nonnull String signalSystem,
069                                        @Nonnull String mastName,
070                                        @Nonnull String[] heads) throws JmriException {
071        StringBuilder name = new StringBuilder(prefix);
072        name.append(":");
073        name.append(signalSystem);
074        name.append(":");
075        name.append(mastName);
076        for (String s : heads) {
077            name.append("(");
078            name.append(StringUtil.parenQuote(s));
079            name.append(")");
080        }
081        return provideSignalMast(new String(name));
082    }
083
084    /** {@inheritDoc} */
085    @Override
086    @Nonnull
087    public SignalMast provideSignalMast(@Nonnull String name) throws IllegalArgumentException {
088        SignalMast m = getSignalMast(name);
089        if (m == null) {
090            // this should be replaced by a Service based approach,
091            // perhaps along the lines of SignalMastAddPane, but
092            // for now we manually check types
093            if (name.startsWith("IF$shsm")) {
094                m = new SignalHeadSignalMast(name);
095            } else if (name.startsWith("IF$dsm")) {
096                m = new DccSignalMast(name);
097            } else if (name.startsWith("IF$vsm")) {
098                m = new VirtualSignalMast(name);
099            } else {
100                // didn't recognize name, so trying to make it virtual
101                log.warn("building stand-in VirtualSignalMast for {}", name);
102                m = new VirtualSignalMast(name);
103            }
104            register(m);
105        }
106        return m;
107    }
108
109    /** {@inheritDoc} */
110    @Nonnull
111    @Override
112    public SignalMast provideCustomSignalMast(@Nonnull String systemName, Class<? extends
113            SignalMast> mastClass) throws JmriException {
114        SignalMast m = getBySystemName(systemName);
115        if (m != null) {
116            if (!mastClass.isInstance(m)) {
117                throw new JmriException("Could not create signal mast " + systemName + ", because" +
118                        " the system name is already used by a different kind of mast. Expected "
119                        + mastClass.getSimpleName() + ", actual " + m.getClass().getSimpleName()
120                        + ".");
121            }
122            return m;
123        }
124        try {
125            m = mastClass.getConstructor(String.class).newInstance(systemName);
126        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
127                InvocationTargetException e) {
128            throw new JmriException(e);
129        }
130        register(m);
131        return m;
132    }
133
134    /** {@inheritDoc} */
135    @Override
136    @CheckForNull
137    public SignalMast getBySystemName(@Nonnull String key) {
138        return _tsys.get(key);
139    }
140
141    /** {@inheritDoc} */
142    @Override
143    @CheckForNull
144    public SignalMast getByUserName(@Nonnull String key) {
145        return _tuser.get(key);
146    }
147
148   @Override
149   @Nonnull
150    public String getBeanTypeHandled(boolean plural) {
151        return Bundle.getMessage(plural ? "BeanNameSignalMasts" : "BeanNameSignalMast");
152    }
153
154    /**
155     * {@inheritDoc}
156     */
157    @Override
158    public Class<SignalMast> getNamedBeanClass() {
159        return SignalMast.class;
160    }
161
162    private final ArrayList<SignalMastRepeater> repeaterList;
163
164    /**
165     * Creates or retrieves a signal mast repeater.
166     * @param master the mast for the master of the repeater.
167     * @param slave the mast for the slave of the repeater.
168     * @return newly created (and registered) or existing signal mast repeater.
169     * @throws JmriException if the repeater already exists but the other direction.
170     */
171    public @Nonnull SignalMastRepeater provideRepeater(@Nonnull SignalMast master, @Nonnull SignalMast
172            slave) throws JmriException {
173        SignalMastRepeater rp = null;
174        for (SignalMastRepeater currentRepeater : repeaterList) {
175            if (currentRepeater.getMasterMast() == master && currentRepeater.getSlaveMast() == slave) {
176                rp = currentRepeater;
177            } else if (currentRepeater.getMasterMast() == slave
178                    && currentRepeater.getSlaveMast() == master) {
179                log.error("Signal repeater {}:{} already exists the wrong way", master, slave);
180                throw new JmriException("Signal mast repeater already exists the wrong way");
181            }
182        }
183        if (rp == null) {
184            rp = new SignalMastRepeater(master, slave);
185            repeaterList.add(rp);
186        }
187        firePropertyChange("repeaterlength", null, null);
188        return rp;
189    }
190
191    public void addRepeater(SignalMastRepeater rp) throws JmriException {
192        for (SignalMastRepeater rpeat : repeaterList) {
193            if (rpeat.getMasterMast() == rp.getMasterMast()
194                    && rpeat.getSlaveMast() == rp.getSlaveMast()) {
195                log.error("Signal repeater already Exists");
196                throw new JmriException("Signal mast Repeater already exists");
197            } else if (rpeat.getMasterMast() == rp.getSlaveMast()
198                    && rpeat.getSlaveMast() == rp.getMasterMast()) {
199                log.error("Signal repeater already Exists");
200                throw new JmriException("Signal mast Repeater already exists");
201            }
202        }
203        repeaterList.add(rp);
204        firePropertyChange("repeaterlength", null, null);
205    }
206
207    public void removeRepeater(SignalMastRepeater rp) {
208        rp.dispose();
209        repeaterList.remove(rp);
210        firePropertyChange("repeaterlength", null, null);
211    }
212
213    public List<SignalMastRepeater> getRepeaterList() {
214        return repeaterList;
215    }
216
217    public void initialiseRepeaters() {
218        for (SignalMastRepeater smr : repeaterList) {
219            smr.initialise();
220        }
221    }
222
223    /** {@inheritDoc} */
224    @Nonnull
225    @Override
226    public SignalMast provide(String name) throws IllegalArgumentException {
227        return provideSignalMast(name);
228    }
229
230    /** {@inheritDoc} */
231    @Override
232    public void dispose(){
233        InstanceManager.getDefault(SignalHeadManager.class).removeVetoableChangeListener(this);
234        InstanceManager.getDefault(TurnoutManager.class).removeVetoableChangeListener(this);
235        super.dispose();
236    }
237
238    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultSignalMastManager.class);
239}