001package jmri.jmrit.logixng.implementation;
002
003import java.beans.*;
004import java.io.PrintWriter;
005import java.util.*;
006
007import javax.annotation.Nonnull;
008import javax.annotation.OverridingMethodsMustInvokeSuper;
009
010import jmri.*;
011import jmri.jmrit.logixng.*;
012import jmri.managers.AbstractManager;
013import jmri.util.LoggingUtil;
014import jmri.util.ThreadingUtil;
015
016/**
017 * Class providing the basic logic of the GlobalVariable_Manager interface.
018 *
019 * @author Dave Duchamp       Copyright (C) 2007
020 * @author Daniel Bergqvist   Copyright (C) 2022
021 */
022public class DefaultGlobalVariableManager extends AbstractManager<GlobalVariable>
023        implements GlobalVariableManager {
024
025
026    public DefaultGlobalVariableManager() {
027        // The GlobalVariablePreferences class may load plugins so we must ensure
028        // it's loaded here.
029        InstanceManager.getDefault(LogixNGPreferences.class);
030    }
031
032    @Override
033    public int getXMLOrder() {
034        return LOGIXNG_GLOBAL_VARIABLES;
035    }
036
037    @Override
038    public char typeLetter() {
039        return 'Q';
040    }
041
042    /**
043     * Test if parameter is a properly formatted system name.
044     *
045     * @param systemName the system name
046     * @return enum indicating current validity, which might be just as a prefix
047     */
048    @Override
049    public NameValidity validSystemNameFormat(String systemName) {
050        return LogixNG_Manager.validSystemNameFormat(
051                getSubSystemNamePrefix(), systemName);
052    }
053
054    /**
055     * Method to create a new GlobalVariable if the GlobalVariable does not exist.
056     * <p>
057     * Returns null if a GlobalVariable with the same systemName or userName
058     * already exists, or if there is trouble creating a new GlobalVariable.
059     */
060    @Override
061    public GlobalVariable createGlobalVariable(String systemName, String userName)
062            throws IllegalArgumentException {
063
064        // Check that GlobalVariable does not already exist
065        GlobalVariable x;
066        if (userName != null && !userName.equals("")) {
067            x = getByUserName(userName);
068            if (x != null) {
069                return null;
070            }
071        }
072        x = getBySystemName(systemName);
073        if (x != null) {
074            return null;
075        }
076        // Check if system name is valid
077        if (this.validSystemNameFormat(systemName) != NameValidity.VALID) {
078            throw new IllegalArgumentException("SystemName " + systemName + " is not in the correct format");
079        }
080        // GlobalVariable does not exist, create a new GlobalVariable
081        x = new DefaultGlobalVariable(systemName, userName);
082        // save in the maps
083        register(x);
084
085        // Keep track of the last created auto system name
086        updateAutoNumber(systemName);
087
088        return x;
089    }
090
091    @Override
092    public GlobalVariable createGlobalVariable(String userName) throws IllegalArgumentException {
093        return createGlobalVariable(getAutoSystemName(), userName);
094    }
095
096    @Override
097    public GlobalVariable getGlobalVariable(String name) {
098        GlobalVariable x = getByUserName(name);
099        if (x != null) {
100            return x;
101        }
102        return getBySystemName(name);
103    }
104
105    @Override
106    public GlobalVariable getByUserName(String name) {
107        return _tuser.get(name);
108    }
109
110    @Override
111    public GlobalVariable getBySystemName(String name) {
112        return _tsys.get(name);
113    }
114
115    /** {@inheritDoc} */
116    @Override
117    public String getBeanTypeHandled(boolean plural) {
118        return Bundle.getMessage(plural ? "BeanNameGlobalVariables" : "BeanNameGlobalVariable");
119    }
120
121    /** {@inheritDoc} */
122    @Override
123    public void deleteGlobalVariable(GlobalVariable x) {
124        // delete the GlobalVariable
125        deregister(x);
126        x.dispose();
127    }
128
129    /** {@inheritDoc} */
130    @Override
131    public void printTree(Locale locale, PrintWriter writer, String indent) {
132        for (GlobalVariable globalVariable : getNamedBeanSet()) {
133            writer.append(String.format(
134                    "Global variable: System name: %s, User name: %s, Initial value type: %s, Initial value data: %s",
135                    globalVariable.getSystemName(), globalVariable.getUserName(),
136                    globalVariable.getInitialValueType().toString(), globalVariable.getInitialValueData()));
137            writer.println();
138        }
139        writer.println();
140    }
141
142    static volatile DefaultGlobalVariableManager _instance = null;
143
144    @InvokeOnGuiThread  // this method is not thread safe
145    static public DefaultGlobalVariableManager instance() {
146        if (!ThreadingUtil.isGUIThread()) {
147            LoggingUtil.warnOnce(log, "instance() called on wrong thread");
148        }
149
150        if (_instance == null) {
151            _instance = new DefaultGlobalVariableManager();
152        }
153        return (_instance);
154    }
155
156    /** {@inheritDoc} */
157    @Override
158    public Class<GlobalVariable> getNamedBeanClass() {
159        return GlobalVariable.class;
160    }
161
162    /**
163     * Inform all registered listeners of a vetoable change.If the propertyName
164     * is "CanDelete" ALL listeners with an interest in the bean will throw an
165     * exception, which is recorded returned back to the invoking method, so
166     * that it can be presented back to the user.However if a listener decides
167     * that the bean can not be deleted then it should throw an exception with
168     * a property name of "DoNotDelete", this is thrown back up to the user and
169     * the delete process should be aborted.
170     *
171     * @param p   The programmatic name of the property that is to be changed.
172     *            "CanDelete" will inquire with all listeners if the item can
173     *            be deleted. "DoDelete" tells the listener to delete the item.
174     * @param old The old value of the property.
175     * @throws java.beans.PropertyVetoException If the recipients wishes the
176     *                                          delete to be aborted (see above)
177     */
178    @OverridingMethodsMustInvokeSuper
179    public void fireVetoableChange(String p, Object old) throws PropertyVetoException {
180        PropertyChangeEvent evt = new PropertyChangeEvent(this, p, old, null);
181        for (VetoableChangeListener vc : vetoableChangeSupport.getVetoableChangeListeners()) {
182            vc.vetoableChange(evt);
183        }
184    }
185
186    /** {@inheritDoc} */
187    @Override
188//    @OverridingMethodsMustInvokeSuper
189    public final void deleteBean(@Nonnull GlobalVariable globalVariable, @Nonnull String property) throws PropertyVetoException {
190        // throws PropertyVetoException if vetoed
191        fireVetoableChange(property, globalVariable);
192        if (property.equals("DoDelete")) { // NOI18N
193            deregister(globalVariable);
194            globalVariable.dispose();
195        }
196    }
197
198
199    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultGlobalVariableManager.class);
200
201}