001package jmri.jmrit.logixng.implementation;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Map;
006import java.util.HashMap;
007import java.util.Locale;
008
009import javax.annotation.CheckForNull;
010
011import jmri.*;
012import jmri.jmrit.logixng.*;
013
014/**
015 * Default implementation of the FemaleGenericExpressionSocket
016 */
017public class DefaultFemaleGenericExpressionSocket
018        extends AbstractFemaleSocket
019        implements FemaleGenericExpressionSocket, FemaleSocketListener {
020
021    private SocketType _socketType;             // The type of the socket the user has selected
022    private SocketType _currentSocketType;      // The current type of the socket.
023    private FemaleSocket _currentActiveSocket;  // The socket that is currently in use, if any. Null otherwise.
024    private final FemaleAnalogExpressionSocket _analogSocket = new DefaultFemaleAnalogExpressionSocket(this, this, "A");
025    private final FemaleDigitalExpressionSocket _digitalSocket = new DefaultFemaleDigitalExpressionSocket(this, this, "D");
026    private final FemaleStringExpressionSocket _stringSocket = new DefaultFemaleStringExpressionSocket(this, this, "S");
027    private boolean _do_i18n;
028
029    public DefaultFemaleGenericExpressionSocket(
030            SocketType socketType,
031            Base parent,
032            FemaleSocketListener listener,
033            String name) {
034
035        super(parent, listener, name);
036
037        _socketType = socketType;
038        _currentSocketType = socketType;
039
040        switch (_socketType) {
041            case ANALOG:
042                _currentActiveSocket = _analogSocket;
043                break;
044
045            case DIGITAL:
046                _currentActiveSocket = _digitalSocket;
047                break;
048
049            case STRING:
050                _currentActiveSocket = _stringSocket;
051                break;
052
053            case GENERIC:
054                _currentActiveSocket = null;
055                break;
056
057            default:
058                throw new RuntimeException("_socketType has invalid value: "+socketType.name());
059        }
060    }
061
062
063    /** {@inheritDoc} */
064    @Override
065    public FemaleSocket getCurrentActiveSocket() {
066        return _currentActiveSocket;
067    }
068
069
070    /** {@inheritDoc} */
071    @Override
072    public boolean isCompatible(MaleSocket socket) {
073        return (socket instanceof MaleAnalogExpressionSocket)
074                || (socket instanceof MaleDigitalExpressionSocket)
075                || (socket instanceof MaleStringExpressionSocket);
076    }
077
078    /** {@inheritDoc} */
079    @Override
080    public void setSocketType(SocketType socketType)
081            throws SocketAlreadyConnectedException {
082
083        if (socketType == _socketType) {
084            return;
085        }
086
087        if ((_currentActiveSocket != null) && (_currentActiveSocket.isConnected())) {
088            throw new SocketAlreadyConnectedException("Socket is already connected");
089        }
090
091        switch (socketType) {
092            case DIGITAL:
093                _socketType = SocketType.DIGITAL;
094                _currentSocketType = SocketType.DIGITAL;
095                _currentActiveSocket = _digitalSocket;
096                break;
097
098            case ANALOG:
099                _socketType = SocketType.ANALOG;
100                _currentSocketType = SocketType.ANALOG;
101                _currentActiveSocket = _analogSocket;
102                break;
103
104            case STRING:
105                _socketType = SocketType.STRING;
106                _currentSocketType = SocketType.STRING;
107                _currentActiveSocket = _stringSocket;
108                break;
109
110            case GENERIC:
111                _socketType = SocketType.GENERIC;
112                _currentSocketType = SocketType.GENERIC;
113                _currentActiveSocket = null;
114                break;
115
116            default:
117                throw new RuntimeException("socketType has invalid value: "+socketType.name());
118        }
119    }
120
121    /** {@inheritDoc} */
122    @Override
123    public SocketType getSocketType() {
124        return _socketType;
125    }
126
127    public void setDoI18N(boolean do_i18n) {
128        _do_i18n = do_i18n;
129    }
130
131    public boolean getDoI18N() {
132        return _do_i18n;
133    }
134
135    @Override
136    @CheckForNull
137    public Object evaluateGeneric() throws JmriException {
138        if (isConnected()) {
139            switch (_currentSocketType) {
140                case DIGITAL:
141                    return ((MaleDigitalExpressionSocket)getConnectedSocket())
142                            .evaluate();
143
144                case ANALOG:
145                    return ((MaleAnalogExpressionSocket)getConnectedSocket())
146                            .evaluate();
147
148                case STRING:
149                    return ((MaleStringExpressionSocket)getConnectedSocket())
150                            .evaluate();
151
152                default:
153                    throw new RuntimeException("_currentSocketType has invalid value: "+_currentSocketType.name());
154            }
155        } else {
156            return null;
157        }
158    }
159
160    /** {@inheritDoc} */
161    @Override
162    public String getShortDescription(Locale locale) {
163        return Bundle.getMessage(locale, "DefaultFemaleGenericExpressionSocket_Short");
164    }
165
166    /** {@inheritDoc} */
167    @Override
168    public String getLongDescription(Locale locale) {
169        return Bundle.getMessage(locale, "DefaultFemaleGenericExpressionSocket_Long", getName());
170    }
171
172    private void addClassesToMap(
173            Map<Category, List<Class<? extends Base>>> destinationClasses,
174            Map<Category, List<Class<? extends Base>>> sourceClasses) {
175
176        for (Category category : Category.values()) {
177            // Some categories might not have any expression.
178            if (sourceClasses.get(category) == null) continue;
179
180            for (Class<? extends Base> clazz : sourceClasses.get(category)) {
181                destinationClasses.get(category).add(clazz);
182            }
183        }
184    }
185
186    @Override
187    public Map<Category, List<Class<? extends Base>>> getConnectableClasses() {
188        Map<Category, List<Class<? extends Base>>> classes = new HashMap<>();
189
190        for (Category category : Category.values()) {
191            classes.put(category, new ArrayList<>());
192        }
193
194        addClassesToMap(classes, InstanceManager.getDefault(AnalogExpressionManager.class).getExpressionClasses());
195        addClassesToMap(classes, InstanceManager.getDefault(DigitalExpressionManager.class).getExpressionClasses());
196        addClassesToMap(classes, InstanceManager.getDefault(StringExpressionManager.class).getExpressionClasses());
197
198        return classes;
199    }
200
201    /** {@inheritDoc} */
202    @Override
203    public void connect(MaleSocket socket) throws SocketAlreadyConnectedException {
204
205        if (socket == null) {
206            throw new NullPointerException("socket cannot be null");
207        }
208
209        // If _currentActiveSocket is not null, the socket is either connected
210        // or locked to a particular type.
211        if (_currentActiveSocket != null) {
212            if (_currentActiveSocket.isConnected()) {
213                throw new SocketAlreadyConnectedException("Socket is already connected");
214            } else {
215                _currentActiveSocket.connect(socket);
216                _listener.connected(this);
217                return;
218            }
219        }
220
221        // If we are here, the socket is not connected and is not locked to a
222        // particular type.
223
224        if (_digitalSocket.isCompatible(socket)) {
225            _currentSocketType = SocketType.DIGITAL;
226            _currentActiveSocket = _digitalSocket;
227        } else if (_analogSocket.isCompatible(socket)) {
228            _currentSocketType = SocketType.ANALOG;
229            _currentActiveSocket = _analogSocket;
230        } else if (_stringSocket.isCompatible(socket)) {
231            _currentSocketType = SocketType.STRING;
232            _currentActiveSocket = _stringSocket;
233        } else {
234            throw new IllegalArgumentException("Socket is not compatible");
235        }
236        _currentActiveSocket.connect(socket);
237        _listener.connected(this);
238    }
239
240    /** {@inheritDoc} */
241    @Override
242    public void disconnect() {
243        if ((_currentActiveSocket != null)
244                && _currentActiveSocket.isConnected()) {
245
246            _currentActiveSocket.disconnect();
247            _listener.disconnected(this);
248        }
249    }
250
251    /** {@inheritDoc} */
252    @Override
253    public MaleSocket getConnectedSocket() {
254        if (_currentActiveSocket != null) {
255            return _currentActiveSocket.getConnectedSocket();
256        } else {
257            return null;
258        }
259    }
260
261    /** {@inheritDoc} */
262    @Override
263    public boolean isConnected() {
264        return (_currentActiveSocket != null) && _currentActiveSocket.isConnected();
265    }
266
267    @Override
268    public void connected(FemaleSocket socket) {
269        // Do nothing
270    }
271
272    @Override
273    public void disconnected(FemaleSocket socket) {
274        if (_socketType == SocketType.GENERIC) {
275            _currentActiveSocket = null;
276        }
277    }
278
279    /** {@inheritDoc} */
280    @Override
281    public void disposeMe() {
282        // Do nothing
283    }
284
285}