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