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