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