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