001package jmri.jmrit.logixng.implementation;
002
003import java.io.PrintWriter;
004import java.util.*;
005
006import jmri.InstanceManager;
007import jmri.JmriException;
008import jmri.Manager;
009import jmri.NamedBean;
010import jmri.NamedBeanUsageReport;
011import jmri.jmrit.logixng.*;
012import jmri.jmrit.logixng.Module;
013import jmri.jmrit.logixng.ModuleManager;
014import jmri.jmrit.logixng.SymbolTable.InitialValueType;
015import jmri.jmrit.logixng.SymbolTable.VariableData;
016
017import org.apache.commons.lang3.mutable.MutableInt;
018
019/**
020 * The default implementation of Module.
021 *
022 * @author Daniel Bergqvist Copyright 2018
023 */
024public class DefaultModule extends AbstractBase
025        implements Module, FemaleSocketListener {
026
027
028    private boolean _isVisible;
029    private boolean _storeIfEmpty;
030    private final FemaleSocketManager.SocketType _rootSocketType;
031    private final FemaleSocket _femaleRootSocket;
032    private String _socketSystemName = null;
033    private final List<Parameter> _parameters = new ArrayList<>();
034    private final List<VariableData> _localVariables = new ArrayList<>();
035    private final Map<Thread, ConditionalNG> _currentConditionalNG = new HashMap<>();
036
037
038    public DefaultModule(String sys, String user, FemaleSocketManager.SocketType socketType)
039            throws BadUserNameException, BadSystemNameException  {
040        this(sys, user, socketType, true, true);
041    }
042
043    public DefaultModule(String sys, String user, FemaleSocketManager.SocketType socketType,
044            boolean isVisible, boolean storeIfEmpty)
045            throws BadUserNameException, BadSystemNameException  {
046
047        super(sys, user);
048
049        this._isVisible = isVisible;
050        this._storeIfEmpty = storeIfEmpty;
051
052        _rootSocketType = socketType;
053        _femaleRootSocket = socketType.createSocket(this, this, "Root");
054
055        // Listeners should never be enabled for a module
056        _femaleRootSocket.setEnableListeners(false);
057
058        // Do this test here to ensure all the tests are using correct system names
059        Manager.NameValidity isNameValid = InstanceManager.getDefault(ModuleManager.class).validSystemNameFormat(mSystemName);
060        if (isNameValid != Manager.NameValidity.VALID) {
061            throw new IllegalArgumentException("system name is not valid");
062        }
063    }
064
065    @Override
066    public boolean isVisible() {
067        return _isVisible;
068    }
069
070    @Override
071    public void setVisible(boolean value) {
072        _isVisible = value;
073    }
074
075    @Override
076    public boolean isStoreIfEmpty() {
077        return _storeIfEmpty;
078    }
079
080    @Override
081    public void setStoreIfEmpty(boolean value) {
082        _storeIfEmpty = value;
083    }
084
085    @Override
086    public void setCurrentConditionalNG(ConditionalNG conditionalNG) {
087        synchronized(this) {
088            _currentConditionalNG.put(Thread.currentThread(), conditionalNG);
089        }
090    }
091
092    @Override
093    public ConditionalNG getConditionalNG() {
094        synchronized(this) {
095            return _currentConditionalNG.get(Thread.currentThread());
096        }
097    }
098
099    /** {@inheritDoc} */
100    @Override
101    public Base getParent() {
102        return null;
103    }
104
105    /** {@inheritDoc} */
106    @Override
107    public void setParent(Base parent) {
108        throw new UnsupportedOperationException("A Module cannot have a parent");
109    }
110
111    @Override
112    public String getBeanType() {
113        return Bundle.getMessage("BeanNameModule");
114    }
115
116    @Override
117    public void setState(int s) throws JmriException {
118        log.warn("Unexpected call to setState in DefaultModule.");  // NOI18N
119    }
120
121    @Override
122    public int getState() {
123        log.warn("Unexpected call to getState in DefaultModule.");  // NOI18N
124        return UNKNOWN;
125    }
126
127    @Override
128    public String getShortDescription(Locale locale) {
129        return Bundle.getMessage("DefaultModule_Short");
130    }
131
132    @Override
133    public String getLongDescription(Locale locale) {
134        StringBuilder sb = new StringBuilder(Bundle.getMessage("DefaultModule_Long", getDisplayName()));
135        if (! _parameters.isEmpty()) {
136            List<String> inParams = new ArrayList<>();
137            List<String> outParams = new ArrayList<>();
138            List<String> inOutParams = new ArrayList<>();
139
140            for (Parameter p : _parameters) {
141                if (p.isInput() && p.isOutput()) inOutParams.add(p.getName());
142                else if (p.isInput()) inParams.add(p.getName());
143                else if (p.isOutput()) outParams.add(p.getName());
144                else throw new RuntimeException("Parameter "+p.getName()+" is neither in or out");
145            }
146            sb.append(" ::: ");
147
148            boolean addComma = false;
149            for (int i=0; i < inParams.size(); i++) {
150                if (i==0) {
151                    sb.append(Bundle.getMessage("DefaultModuleParamInput"));
152                    addComma = true;
153                }
154                else sb.append(", ");
155                sb.append(inParams.get(i));
156            }
157
158            if (addComma) sb.append(", ");
159            addComma = false;
160
161            for (int i=0; i < outParams.size(); i++) {
162                if (i==0) {
163                    sb.append(Bundle.getMessage("DefaultModuleParamOuput"));
164                    addComma = true;
165                }
166                else sb.append(", ");
167                sb.append(outParams.get(i));
168            }
169
170            if (addComma) sb.append(", ");
171
172            for (int i=0; i < inOutParams.size(); i++) {
173                if (i==0) sb.append(Bundle.getMessage("DefaultModuleParamInputOutput"));
174                else sb.append(", ");
175                sb.append(inOutParams.get(i));
176            }
177        }
178        return sb.toString();
179    }
180
181    @Override
182    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
183        if (index != 0) {
184            throw new IllegalArgumentException(
185                    String.format("index has invalid value: %d", index));
186        }
187
188        return _femaleRootSocket;
189    }
190
191    @Override
192    public int getChildCount() {
193        return 1;
194    }
195
196    @Override
197    public LogixNG_Category getCategory() {
198        return LogixNG_Category.OTHER;
199    }
200/*
201    protected void printTreeRow(Locale locale, PrintWriter writer, String currentIndent) {
202        writer.append(currentIndent);
203        writer.append(getLongDescription(locale));
204        writer.println();
205    }
206*/
207    /** {@inheritDoc} */
208    @Override
209    public void printTree(
210            PrintTreeSettings settings,
211            PrintWriter writer,
212            String indent,
213            MutableInt lineNumber) {
214
215        printTree(settings, Locale.getDefault(), writer, indent, "", lineNumber);
216    }
217
218    /** {@inheritDoc} */
219    @Override
220    public void printTree(
221            PrintTreeSettings settings,
222            Locale locale,
223            PrintWriter writer,
224            String indent,
225            MutableInt lineNumber) {
226
227        printTree(settings, locale, writer, indent, "", lineNumber);
228    }
229
230    /** {@inheritDoc} */
231    @Override
232    public void printTree(
233            PrintTreeSettings settings,
234            Locale locale,
235            PrintWriter writer,
236            String indent,
237            String currentIndent,
238            MutableInt lineNumber) {
239
240        printTreeRow(settings, locale, writer, currentIndent, lineNumber);
241
242        _femaleRootSocket.printTree(settings, locale, writer, indent, currentIndent+indent, lineNumber);
243    }
244/*
245    @Override
246    public void setRootSocketType(FemaleSocketManager.SocketType socketType) {
247        if ((_femaleRootSocket != null) && _femaleRootSocket.isConnected()) throw new RuntimeException("Cannot set root socket when it's connected");
248
249        _rootSocketType = socketType;
250        _femaleRootSocket = socketType.createSocket(this, this, "Root");
251
252        // Listeners should never be enabled for a module
253        _femaleRootSocket.setEnableListeners(false);
254    }
255*/
256    @Override
257    public FemaleSocketManager.SocketType getRootSocketType() {
258        return _rootSocketType;
259    }
260
261    @Override
262    public FemaleSocket getRootSocket() {
263        return _femaleRootSocket;
264    }
265
266    @Override
267    public void addParameter(String name, boolean isInput, boolean isOutput) {
268        _parameters.add(new DefaultSymbolTable.DefaultParameter(name, isInput, isOutput));
269    }
270
271    @Override
272    public void addParameter(Parameter parameter) {
273        _parameters.add(parameter);
274    }
275
276//    @Override
277//    public void removeParameter(String name) {
278//        _parameters.remove(name);
279//    }
280
281    @Override
282    public void addLocalVariable(
283            String name,
284            InitialValueType initialValueType,
285            String initialValueData) {
286
287        _localVariables.add(new VariableData(
288                name,
289                initialValueType,
290                initialValueData));
291    }
292
293//    @Override
294//    public void removeLocalVariable(String name) {
295//        _localVariables.remove(name);
296//    }
297
298    @Override
299    public List<Parameter> getParameters() {
300        return _parameters;
301    }
302
303    @Override
304    public List<VariableData> getLocalVariables() {
305        return _localVariables;
306    }
307
308    @Override
309    public void connected(FemaleSocket socket) {
310        _socketSystemName = socket.getConnectedSocket().getSystemName();
311    }
312
313    @Override
314    public void disconnected(FemaleSocket socket) {
315        _socketSystemName = null;
316    }
317
318    public void setSocketSystemName(String systemName) {
319        if ((systemName == null) || (!systemName.equals(_socketSystemName))) {
320            _femaleRootSocket.disconnect();
321        }
322        _socketSystemName = systemName;
323    }
324
325    public String getSocketSystemName() {
326        return _socketSystemName;
327    }
328
329    /** {@inheritDoc} */
330    @Override
331    final public void setup() {
332        if (!_femaleRootSocket.isConnected()
333                || !_femaleRootSocket.getConnectedSocket().getSystemName()
334                        .equals(_socketSystemName)) {
335
336            _femaleRootSocket.disconnect();
337
338            if (_socketSystemName != null) {
339                try {
340                    MaleSocket maleSocket =
341                            _rootSocketType.getManager()
342                                    .getBySystemName(_socketSystemName);
343                    if (maleSocket != null) {
344                        _femaleRootSocket.connect(maleSocket);
345                        maleSocket.setup();
346                    } else {
347                        log.error("digital action is not found: {}", _socketSystemName);
348                    }
349                } catch (SocketAlreadyConnectedException ex) {
350                    // This shouldn't happen and is a runtime error if it does.
351                    throw new RuntimeException("socket is already connected");
352                }
353            }
354        } else {
355            _femaleRootSocket.setup();
356        }
357    }
358
359    /** {@inheritDoc} */
360    @Override
361    final public void disposeMe() {
362        _femaleRootSocket.dispose();
363    }
364
365    @Override
366    protected void registerListenersForThisClass() {
367        // Do nothing. A module never listen on anything.
368    }
369
370    @Override
371    protected void unregisterListenersForThisClass() {
372        // Do nothing. A module never listen on anything.
373    }
374
375    @Override
376    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) {
377        throw new UnsupportedOperationException("Not supported yet.");
378    }
379
380    /** {@inheritDoc} */
381    @Override
382    public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) {
383        List<NamedBeanUsageReport> report = new ArrayList<>();
384        if (bean != null) {
385            getUsageTree(0, bean, report, null);
386        }
387        return report;
388    }
389
390    /** {@inheritDoc} */
391    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
392                                                        justification="Specific log message format")
393    @Override
394    public void getUsageTree(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) {
395        log.debug("** {} :: {}", level, this.getClass().getName());
396        level++;
397        _femaleRootSocket.getUsageTree(level, bean, report, cdl);
398    }
399
400    @Override
401    public boolean existsInTree() {
402        return true;
403    }
404
405    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultModule.class);
406
407}