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