001package jmri.jmrix.openlcb;
002
003import java.util.*;
004
005import javax.annotation.Nonnull;
006import jmri.BooleanPropertyDescriptor;
007import jmri.JmriException;
008import jmri.NamedBean;
009import jmri.NamedBeanPropertyDescriptor;
010import jmri.Turnout;
011import jmri.jmrix.can.CanSystemConnectionMemo;
012import jmri.managers.AbstractTurnoutManager;
013
014/**
015 * OpenLCB implementation of a TurnoutManager.
016 * <p>
017 * Turnouts must be manually created.
018 *
019 * @author Bob Jacobsen Copyright (C) 2008, 2010
020 * @since 2.3.1
021 */
022public class OlcbTurnoutManager extends AbstractTurnoutManager {
023
024    public OlcbTurnoutManager(CanSystemConnectionMemo memo) {
025        super(memo);
026    }
027
028    // Whether we accumulate partially loaded turnouts in pendingTurnouts.
029    private boolean isLoading = false;
030    // Turnouts that are being loaded from XML.
031    private final ArrayList<OlcbTurnout> pendingTurnouts = new ArrayList<>();
032
033    /**
034     * {@inheritDoc}
035     */
036    @Override
037    @Nonnull
038    public CanSystemConnectionMemo getMemo() {
039        return (CanSystemConnectionMemo) memo;
040    }
041
042    @Override
043    @Nonnull
044    public List<NamedBeanPropertyDescriptor<?>> getKnownBeanProperties() {
045        List<NamedBeanPropertyDescriptor<?>> l = new ArrayList<>();
046        l.add(new BooleanPropertyDescriptor(OlcbUtils.PROPERTY_IS_AUTHORITATIVE, OlcbTurnout
047                .DEFAULT_IS_AUTHORITATIVE) {
048            @Override
049            public String getColumnHeaderText() {
050                return Bundle.getMessage("OlcbStateAuthHeader");
051            }
052
053            @Override
054            public boolean isEditable(NamedBean bean) {
055                return OlcbUtils.isOlcbBean(bean);
056            }
057        });
058        l.add(new BooleanPropertyDescriptor(OlcbUtils.PROPERTY_LISTEN, OlcbTurnout
059                .DEFAULT_LISTEN) {
060            @Override
061            public String getColumnHeaderText() {
062                return Bundle.getMessage("OlcbStateListenHeader");
063            }
064
065            @Override
066            public boolean isEditable(NamedBean bean) {
067                return OlcbUtils.isOlcbBean(bean);
068            }
069        });
070        return l;
071    }
072
073    /**
074     * Internal method to invoke the factory, after all the logic for returning
075     * an existing method has been invoked.
076     *
077     * @return never null
078     * {@inheritDoc}
079     */
080    @Nonnull
081    @Override
082    protected Turnout createNewTurnout(@Nonnull String systemName, String userName) throws IllegalArgumentException {
083        String addr = systemName.substring(getSystemPrefix().length() + 1);
084        OlcbTurnout t = new OlcbTurnout(getSystemPrefix(), addr, (CanSystemConnectionMemo) memo);
085        t.setUserName(userName);
086        synchronized (pendingTurnouts) {
087            if (isLoading) {
088                pendingTurnouts.add(t);
089            } else {
090                t.finishLoad();
091            }
092        }
093        return t;
094    }
095
096    /**
097     * This function is invoked before an XML load is started. We defer initialization of the
098     * newly created turnouts until finishLoad because the feedback type might be changing as we
099     * are parsing the XML.
100     */
101    public void startLoad() {
102        synchronized (pendingTurnouts) {
103            isLoading = true;
104        }
105    }
106
107    /**
108     * This function is invoked after the XML load is complete and all turnouts are instantiated
109     * and their feedback type is read in. We use this hook to finalize the construction of the
110     * OpenLCB objects whose instantiation was deferred until the feedback type was known.
111     */
112    public void finishLoad() {
113        synchronized (pendingTurnouts) {
114            pendingTurnouts.forEach(OlcbTurnout::finishLoad);
115            pendingTurnouts.clear();
116            isLoading = false;
117        }
118    }
119
120    @Override
121    public boolean allowMultipleAdditions(@Nonnull String systemName) {
122        return false;
123    }
124
125    @Override
126    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException {
127        // don't check for integer; should check for validity here
128        String tmpPrefix = prefix + typeLetter();
129        String tmpSName  = tmpPrefix + curAddress;
130        try {
131            OlcbAddress.validateSystemNameFormat(tmpSName,Locale.getDefault(),tmpPrefix, (CanSystemConnectionMemo) memo);
132        } catch (jmri.NamedBean.BadSystemNameException e) {
133            throw new JmriException(e.getMessage());
134        }
135        return prefix + typeLetter() + curAddress;
136    }
137
138    @Override
139    @javax.annotation.Nonnull
140    @javax.annotation.CheckReturnValue
141    public String getNextValidSystemName(@Nonnull NamedBean currentBean) throws JmriException {
142        throw new jmri.JmriException("getNextValidSystemName should not have been called");
143    }
144
145    /**
146     * Validates to OpenLCB 2 part address format.
147     * {@inheritDoc}
148     */
149    @Override
150    @Nonnull
151    public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale)
152        throws jmri.NamedBean.BadSystemNameException {
153        return OlcbAddress.validateSystemNameFormat2Part(super.validateSystemNameFormat(
154            name,locale),locale,getSystemNamePrefix(), (CanSystemConnectionMemo) memo);
155    }
156
157    /**
158     * {@inheritDoc}
159     */
160    @Override
161    public String getEntryToolTip() {
162        return Bundle.getMessage("AddTurnoutEntryToolTip");
163    }
164
165}