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