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}