001package jmri.jmrit.logixng.implementation;
002
003import java.util.*;
004
005import jmri.*;
006import jmri.jmrit.logixng.*;
007
008/**
009 * Have many items of any type.
010 * <P>
011 * This class is used by the clipboard.
012 * 
013 * @author Daniel Bergqvist Copyright 2018
014 */
015public class ClipboardMany extends AbstractBase
016        implements FemaleSocketListener {
017
018    private Base _parent;
019    private final List<ItemEntry> _itemEntries = new ArrayList<>();
020    private boolean disableCheckForUnconnectedSocket = false;
021    
022    public ClipboardMany(String sys, String user)
023            throws BadUserNameException, BadSystemNameException {
024        super(sys, user);
025        _itemEntries.add(new ItemEntry(new DefaultFemaleAnySocket(this, this, getNewSocketName())));
026    }
027
028    public ClipboardMany(String sys, String user, List<ItemData> itemSystemNames)
029            throws BadUserNameException, BadSystemNameException {
030        super(sys, user);
031        setItemSystemNames(itemSystemNames);
032    }
033    
034    private void setItemSystemNames(List<ItemData> systemNamesAndClasses) {
035        if (!_itemEntries.isEmpty()) {
036            throw new RuntimeException("action system names cannot be set more than once");
037        }
038        
039        for (ItemData itemData : systemNamesAndClasses) {
040            FemaleAnySocket socket =
041                    new DefaultFemaleAnySocket(this, this, itemData._socketName);
042            
043            _itemEntries.add(new ItemEntry(socket, itemData._className, itemData._systemName));
044        }
045    }
046
047    public String getItemSystemName(int index) {
048        return _itemEntries.get(index)._socketSystemName;
049    }
050
051    /** {@inheritDoc} */
052    @Override
053    public void setup() {
054        // We don't want to check for unconnected sockets while setup sockets
055        disableCheckForUnconnectedSocket = true;
056        
057        for (ItemEntry ae : _itemEntries) {
058            try {
059                if ( !ae._socket.isConnected()
060                        || !ae._socket.getConnectedSocket().getSystemName()
061                                .equals(ae._socketSystemName)) {
062
063                    String socketSystemName = ae._socketSystemName;
064                    ae._socket.disconnect();
065                    if (socketSystemName != null) {
066                        NamedBean namedBean =
067                                InstanceManager.getDefault(LogixNG_Manager.class)
068                                        .getManager(ae._itemManagerClass).getBySystemName(socketSystemName);
069                        
070                        if (namedBean != null) {
071                            if (namedBean instanceof MaleSocket) {
072                                MaleSocket maleSocket = (MaleSocket)namedBean;
073                                ae._socket.connect(maleSocket);
074                                maleSocket.setup();
075                            } else {
076                                log.error("item {} is not a male socket", socketSystemName);
077                            }
078                        } else {
079                            log.error("cannot load item {}", socketSystemName);
080                        }
081                    }
082                } else {
083                    ae._socket.getConnectedSocket().setup();
084                }
085            } catch (SocketAlreadyConnectedException ex) {
086                // This shouldn't happen and is a runtime error if it does.
087                throw new RuntimeException("socket is already connected");
088            }
089        }
090        
091        disableCheckForUnconnectedSocket = false;
092    }
093    
094    /** {@inheritDoc} */
095    @Override
096    public Category getCategory() {
097        return Category.COMMON;
098    }
099
100    @Override
101    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
102        return _itemEntries.get(index)._socket;
103    }
104
105    @Override
106    public int getChildCount() {
107        return _itemEntries.size();
108    }
109    
110    public void ensureFreeSocketAtTop() {
111        if (_itemEntries.get(0)._socket.isConnected()) {
112            DefaultFemaleAnySocket socket =
113                    new DefaultFemaleAnySocket(this, this, getNewSocketName());
114            _itemEntries.add(0, new ItemEntry(socket));
115            
116            List<FemaleSocket> list = new ArrayList<>();
117            list.add(socket);
118            firePropertyChange(Base.PROPERTY_CHILD_COUNT, null, list);
119        }
120    }
121    
122    @Override
123    public void connected(FemaleSocket socket) {
124        if (disableCheckForUnconnectedSocket) return;
125        
126        for (ItemEntry entry : _itemEntries) {
127            if (socket == entry._socket) {
128                entry._socketSystemName =
129                        socket.getConnectedSocket().getSystemName();
130            }
131        }
132    }
133
134    @Override
135    public void disconnected(FemaleSocket socket) {
136        for (int i=0; i < _itemEntries.size(); i++) {
137            ItemEntry entry = _itemEntries.get(i);
138            if (socket == entry._socket) {
139                entry._socketSystemName = null;
140                
141                // Remove socket if not the first socket
142                if (i > 0) {
143                    List<FemaleSocket> list = new ArrayList<>();
144                    list.add(socket);
145                    _itemEntries.remove(i);
146                    firePropertyChange(Base.PROPERTY_CHILD_COUNT, list, null);
147                }
148                break;
149            }
150        }
151    }
152    
153    @Override
154    public String getShortDescription(Locale locale) {
155        return Bundle.getMessage(locale, "Many_Short");
156    }
157
158    @Override
159    public String getLongDescription(Locale locale) {
160        return Bundle.getMessage(locale, "Many_Long");
161    }
162
163    @Override
164    public void setState(int s) throws JmriException {
165        throw new UnsupportedOperationException("Not supported");
166    }
167
168    @Override
169    public int getState() {
170        throw new UnsupportedOperationException("Not supported");
171    }
172
173    @Override
174    public String getBeanType() {
175        throw new UnsupportedOperationException("Not supported");
176    }
177
178    @Override
179    public Base getParent() {
180        return _parent;
181    }
182
183    @Override
184    public void setParent(Base parent) {
185        _parent = parent;
186    }
187
188    @Override
189    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) {
190        throw new UnsupportedOperationException("Not supported");
191    }
192
193    @Override
194    public Base deepCopyChildren(Base original, Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
195        throw new UnsupportedOperationException("Not supported");
196    }
197    
198    
199    private static class ItemEntry {
200        private String _socketSystemName;
201        private String _itemManagerClass;
202        private final FemaleAnySocket _socket;
203        
204        private ItemEntry(FemaleAnySocket socket, String itemManagerClass, String socketSystemName) {
205            _socketSystemName = socketSystemName;
206            _itemManagerClass = itemManagerClass;
207            _socket = socket;
208        }
209        
210        private ItemEntry(FemaleAnySocket socket) {
211            this._socket = socket;
212        }
213        
214    }
215
216    /** {@inheritDoc} */
217    @Override
218    public void registerListenersForThisClass() {
219        // Do nothing
220    }
221
222    /** {@inheritDoc} */
223    @Override
224    public void unregisterListenersForThisClass() {
225        // Do nothing
226    }
227    
228    private String getNewSocketName() {
229        int x = 1;
230        while (x < 10000) {     // Protect from infinite loop
231            boolean validName = true;
232            for (int i=0; i < getChildCount(); i++) {
233//                String name = "*" + Integer.toString(x);
234                String name = "X" + Integer.toString(x);
235                if (name.equals(getChild(i).getName())) {
236                    validName = false;
237                    break;
238                }
239            }
240            if (validName) {
241//                return "*" + Integer.toString(x);
242                return "X" + Integer.toString(x);
243            }
244            x++;
245        }
246        throw new RuntimeException("Unable to find a new socket name");
247    }
248
249    /** {@inheritDoc} */
250    @Override
251    public void disposeMe() {
252    }
253    
254    
255    public static class ItemData {
256        
257        public final String _systemName;
258        public final String _className;
259        public final String _socketName;
260        
261        public ItemData(String socketName, String systemName, String className) {
262            _systemName = systemName;
263            _className = className;
264            _socketName = socketName;
265        }
266        
267    }
268    
269    
270    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ClipboardMany.class);
271
272}