001package jmri.jmrit.logixng.implementation;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004
005import java.beans.*;
006
007import javax.annotation.Nonnull;
008import javax.annotation.OverridingMethodsMustInvokeSuper;
009
010import jmri.InstanceManager;
011import jmri.InvokeOnGuiThread;
012import jmri.jmrit.logixng.*;
013import jmri.jmrit.logixng.util.LogixNG_Thread;
014import jmri.managers.AbstractManager;
015import jmri.util.*;
016
017/**
018 * Class providing the basic logic of the ConditionalNG_Manager interface.
019 *
020 * @author Dave Duchamp       Copyright (C) 2007
021 * @author Daniel Bergqvist   Copyright (C) 2018
022 * @author Dave Sand          Copyright (C) 2021
023 */
024public class DefaultConditionalNGManager extends AbstractManager<ConditionalNG>
025        implements ConditionalNG_Manager {
026
027
028    public DefaultConditionalNGManager() {
029        // LogixNGPreferences class may load plugins so we must ensure
030        // it's loaded here.
031        InstanceManager.getDefault(LogixNGPreferences.class);
032    }
033
034    /** {@inheritDoc} */
035    @Override
036    public int getXMLOrder() {
037        return LOGIXNG_CONDITIONALNGS;
038    }
039
040    /** {@inheritDoc} */
041    @Override
042    public char typeLetter() {
043        return 'Q';
044    }
045
046    /** {@inheritDoc} */
047    @Override
048    public NameValidity validSystemNameFormat(String systemName) {
049        return LogixNG_Manager.validSystemNameFormat(
050                getSubSystemNamePrefix(), systemName);
051    }
052
053    /** {@inheritDoc} */
054    @Override
055    public ConditionalNG createConditionalNG(LogixNG logixNG, String systemName, String userName)
056            throws IllegalArgumentException {
057
058        return createConditionalNG(logixNG, systemName, userName, LogixNG_Thread.DEFAULT_LOGIXNG_THREAD);
059    }
060
061    /** {@inheritDoc} */
062    @Override
063    public ConditionalNG createConditionalNG(
064            LogixNG logixNG, String systemName, String userName, int threadID)
065            throws IllegalArgumentException {
066
067        // Check that ConditionalNG does not already exist
068        ConditionalNG x;
069        if (userName != null && !userName.isEmpty()) {
070            x = getByUserName(logixNG, userName);
071            if (x != null) {
072                log.error("username '{}' already exists, conditionalNG is '{}'", userName, x.getDisplayName());
073                return null;
074            }
075        }
076
077        x = getBySystemName(systemName);
078        if (x != null) {
079            log.error("systemname '{}' already exists, conditionalNG is '{}'", systemName, x.getDisplayName());
080            return null;
081        }
082
083        // Check if system name is valid
084        if (this.validSystemNameFormat(systemName) != NameValidity.VALID) {
085            throw new IllegalArgumentException("SystemName '" + systemName + "' is not in the correct format");
086        }
087
088        // ConditionalNG does not exist, create a new ConditionalNG
089        x = new DefaultConditionalNG(systemName, userName, threadID);
090
091        // Add the conditional to the LogixNG map
092        logixNG.addConditionalNG(x);
093
094        // Keep track of the last created auto system name
095        updateAutoNumber(systemName);
096
097        return x;
098    }
099
100    /** {@inheritDoc} */
101    @Override
102    public ConditionalNG createConditionalNG(LogixNG logixNG, String userName) throws IllegalArgumentException {
103        return createConditionalNG(logixNG, getAutoSystemName(), userName);
104    }
105
106    /** {@inheritDoc} */
107    @Override
108    public ConditionalNG createConditionalNG(LogixNG logixNG, String userName, int threadID) throws IllegalArgumentException {
109        return createConditionalNG(logixNG, getAutoSystemName(), userName, threadID);
110    }
111
112    /** {@inheritDoc} */
113    @Override
114    public ConditionalNG getConditionalNG(LogixNG logixNG, String name) {
115        if (logixNG != null) {
116            ConditionalNG x = getByUserName(logixNG, name);
117            if (x != null) {
118                return x;
119            }
120        }
121        return getBySystemName(name);
122    }
123
124    /** {@inheritDoc} */
125    @Override
126    public LogixNG getParentLogixNG(String systemName) {
127        if (systemName == null || systemName.isEmpty()) {
128            return null;
129        }
130
131        for (LogixNG logixNG : InstanceManager.getDefault(LogixNG_Manager.class).getNamedBeanSet()) {
132            for (int i = 0; i < logixNG.getNumConditionalNGs(); i++) {
133                if (systemName.equals(logixNG.getConditionalNG_SystemName(i))) {
134                    return logixNG;
135                }
136            }
137        }
138        return null;
139    }
140
141    /** {@inheritDoc} */
142    @Override
143    public ConditionalNG getByUserName(LogixNG logixNG, String name) {
144        if (logixNG != null && name != null && !name.isEmpty()) {
145            for (int i = 0; i < logixNG.getNumConditionalNGs(); i++) {
146                ConditionalNG conditionalNG = logixNG.getConditionalNG(i);
147                if (conditionalNG != null) {
148                    if (name.equals(conditionalNG.getUserName())) {
149                        return conditionalNG;
150                    }
151                }
152            }
153        }
154        return null;
155    }
156
157    /** {@inheritDoc} */
158    @Override
159    public ConditionalNG getBySystemName(String name) {
160        LogixNG logixNG = getParentLogixNG(name);
161        if (logixNG != null) {
162            for (int i = 0; i < logixNG.getNumConditionalNGs(); i++) {
163                if (name.equals(logixNG.getConditionalNG_SystemName(i))) {
164                    return logixNG.getConditionalNG(i);
165                }
166            }
167        }
168        return null;
169    }
170
171    /** {@inheritDoc} */
172    @Override
173    public String getBeanTypeHandled(boolean plural) {
174        return Bundle.getMessage(plural ? "BeanNameConditionalNGs" : "BeanNameConditionalNG");
175    }
176
177    /** {@inheritDoc} */
178    @Override
179    public void deleteConditionalNG(ConditionalNG x) {
180        // delete the ConditionalNG
181        deregister(x);
182        x.dispose();
183    }
184
185    /** {@inheritDoc} */
186    @Override
187    public void setLoadDisabled(boolean s) {
188        throw new UnsupportedOperationException("Not supported yet.");
189    }
190
191    /** {@inheritDoc} */
192    @Override
193    public void setRunOnGUIDelayed(boolean value) {
194        InstanceManager.getDefault(LogixNG_Manager.class).getNamedBeanSet().forEach(logixNG -> {
195            for (int i = 0; i < logixNG.getNumConditionalNGs(); i++) {
196                if (logixNG.getConditionalNG(i) != null) {
197                    logixNG.getConditionalNG(i).setRunDelayed(false);
198                }
199            }
200        });
201    }
202
203    static volatile DefaultConditionalNGManager _instance = null;
204
205    @InvokeOnGuiThread  // this method is not thread safe
206    static public DefaultConditionalNGManager instance() {
207        if (!ThreadingUtil.isGUIThread()) {
208            LoggingUtil.warnOnce(log, "instance() called on wrong thread");
209        }
210
211        if (_instance == null) {
212            _instance = new DefaultConditionalNGManager();
213        }
214        return (_instance);
215    }
216
217    /** {@inheritDoc} */
218    @Override
219    public Class<ConditionalNG> getNamedBeanClass() {
220        return ConditionalNG.class;
221    }
222
223    /**
224     * Inform all registered listeners of a vetoable change.If the propertyName
225     * is "CanDelete" ALL listeners with an interest in the bean will throw an
226     * exception, which is recorded returned back to the invoking method, so
227     * that it can be presented back to the user.However if a listener decides
228     * that the bean can not be deleted then it should throw an exception with
229     * a property name of "DoNotDelete", this is thrown back up to the user and
230     * the delete process should be aborted.
231     *
232     * @param p   The programmatic name of the property that is to be changed.
233     *            "CanDelete" will inquire with all listeners if the item can
234     *            be deleted. "DoDelete" tells the listener to delete the item.
235     * @param old The old value of the property.
236     * @throws java.beans.PropertyVetoException If the recipients wishes the
237     *                                          delete to be aborted (see above)
238     */
239    @OverridingMethodsMustInvokeSuper
240    public void fireVetoableChange(String p, Object old) throws PropertyVetoException {
241        PropertyChangeEvent evt = new PropertyChangeEvent(this, p, old, null);
242        for (VetoableChangeListener vc : vetoableChangeSupport.getVetoableChangeListeners()) {
243            vc.vetoableChange(evt);
244        }
245    }
246
247    /** {@inheritDoc} */
248    @Override
249    @SuppressFBWarnings(value = "OVERRIDING_METHODS_MUST_INVOKE_SUPER",
250            justification = "LogixNG is a tree that must be deleted recursively")
251    public final void deleteBean(@Nonnull ConditionalNG conditionalNG, @Nonnull String property) throws PropertyVetoException {
252        FemaleSocket child = conditionalNG.getFemaleSocket();
253        if (child.isConnected()) {
254            MaleSocket maleSocket = child.getConnectedSocket();
255            maleSocket.getManager().deleteBean(maleSocket, property);
256        }
257
258        // throws PropertyVetoException if vetoed
259        fireVetoableChange(property, conditionalNG);
260        if (property.equals("DoDelete")) { // NOI18N
261            if (conditionalNG.getLogixNG() != null) {
262                conditionalNG.getLogixNG().deleteConditionalNG(conditionalNG);
263            }
264            conditionalNG.dispose();
265        }
266    }
267
268
269    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultConditionalNGManager.class);
270}