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