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