001package jmri.jmrit.logixng.implementation; 002 003import java.io.PrintWriter; 004import java.util.*; 005import java.util.concurrent.ConcurrentHashMap; 006import java.util.concurrent.CopyOnWriteArrayList; 007 008import javax.annotation.CheckForNull; 009import javax.annotation.OverridingMethodsMustInvokeSuper; 010 011import jmri.*; 012import jmri.implementation.AbstractNamedBean; 013import jmri.jmrit.logixng.*; 014import jmri.jmrit.logixng.SymbolTable.InitialValueType; 015 016import org.apache.commons.lang3.mutable.MutableInt; 017 018/** 019 * The default implementation of GlobalVariable. 020 * 021 * @author Daniel Bergqvist Copyright 2018 022 * @author Dave Sand Copyright 2021 023 */ 024public class DefaultGlobalVariable extends AbstractNamedBean 025 implements GlobalVariable { 026 027 private Object _value; 028 private InitialValueType _initialValueType = InitialValueType.None; 029 private String _initialValueData; 030 031 032 public DefaultGlobalVariable(String sys, String user) throws BadUserNameException, BadSystemNameException { 033 super(sys, user); 034 035 // Do this test here to ensure all the tests are using correct system names 036 Manager.NameValidity isNameValid = InstanceManager.getDefault(GlobalVariableManager.class).validSystemNameFormat(mSystemName); 037 if (isNameValid != Manager.NameValidity.VALID) { 038 throw new IllegalArgumentException("system name is not valid"); 039 } 040 } 041 042 /** {@inheritDoc} */ 043 @Override 044 @OverridingMethodsMustInvokeSuper 045 public void setUserName(@CheckForNull String s) throws BadUserNameException { 046 if ((s == null) || !SymbolTable.validateName(s)) { 047 throw new BadUserNameException( 048 Bundle.getMessage(Locale.ENGLISH, "VariableNameIsNotValid", s), 049 Bundle.getMessage(Locale.getDefault(), "VariableNameIsNotValid", s)); 050 } 051 super.setUserName(s); 052 } 053 054 /** {@inheritDoc} */ 055 @Override 056 @SuppressWarnings({"unchecked", "rawtypes"}) // Checked cast is not possible due to type erasure 057 public void initialize() throws JmriException { 058 SymbolTable symbolTable = new DefaultSymbolTable(); 059 060 Object value; 061 062 switch (_initialValueType) { 063 064 case Array: 065 var newArray = SymbolTable.getInitialValue( 066 SymbolTable.Type.Global, 067 getUserName(), 068 _initialValueType, 069 _initialValueData, 070 symbolTable, 071 symbolTable.getSymbols()); 072 073 // Convert the array to a thread safe array 074 // https://howtodoinjava.com/java/collections/arraylist/synchronize-arraylist/ 075 value = new CopyOnWriteArrayList<>((List)newArray); 076 break; 077 078 case Map: 079 // https://crunchify.com/hashmap-vs-concurrenthashmap-vs-synchronizedmap-how-a-hashmap-can-be-synchronized-in-java/ 080 value = new ConcurrentHashMap<>(); 081 break; 082 083 default: 084 value = SymbolTable.getInitialValue( 085 SymbolTable.Type.Global, 086 getUserName(), 087 _initialValueType, 088 _initialValueData, 089 symbolTable, 090 symbolTable.getSymbols()); 091 } 092 093 if (value != _value) setValue(value); 094 } 095 096 /** {@inheritDoc} */ 097 @Override 098 public void setValue(Object value) { 099 Object old = _value; 100 LogixNGPreferences prefs = InstanceManager.getDefault(LogixNGPreferences.class); 101 if (prefs.getStrictTypingLocalVariables()) { 102 _value = SymbolTable.validateStrictTyping(_initialValueType, _value, value); 103 } else { 104 _value = value; 105 } 106 // notify 107 firePropertyChange("value", old, _value); 108 } 109 110 /** {@inheritDoc} */ 111 @Override 112 public Object getValue() { 113 return _value; 114 } 115 116 /** {@inheritDoc} */ 117 @Override 118 public void setInitialValueType(InitialValueType type) { 119 _initialValueType = type; 120 } 121 122 /** {@inheritDoc} */ 123 @Override 124 public InitialValueType getInitialValueType() { 125 return _initialValueType; 126 } 127 128 /** {@inheritDoc} */ 129 @Override 130 public void setInitialValueData(String value) { 131 _initialValueData = value; 132 } 133 134 /** {@inheritDoc} */ 135 @Override 136 public String getInitialValueData() { 137 return _initialValueData; 138 } 139 140 @Override 141 public String getBeanType() { 142 return Bundle.getMessage("BeanNameGlobalVariable"); 143 } 144 145 @Override 146 public void setState(int s) throws JmriException { 147 log.warn("Unexpected call to setState in DefaultGlobalVariable."); // NOI18N 148 } 149 150 @Override 151 public int getState() { 152 log.warn("Unexpected call to getState in DefaultGlobalVariable."); // NOI18N 153 return UNKNOWN; 154 } 155 156 @Override 157 public String getShortDescription(Locale locale) { 158 return "GlobalVariable"; 159 } 160 161 @Override 162 public String getLongDescription(Locale locale) { 163 return "GlobalVariable: "+getDisplayName(); 164 } 165 166 @Override 167 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 168 throw new UnsupportedOperationException("Not supported."); 169 } 170 171 @Override 172 public int getChildCount() { 173 throw new UnsupportedOperationException("Not supported."); 174 } 175 176 @Override 177 public Category getCategory() { 178 throw new UnsupportedOperationException("Not supported."); 179 } 180 181 /** {@inheritDoc} */ 182 @Override 183 public boolean isActive() { 184 return true; 185 } 186 187 /** {@inheritDoc} */ 188 @Override 189 public Base getParent() { 190 return null; 191 } 192 193 /** {@inheritDoc} */ 194 @Override 195 public void setParent(Base parent) { 196 throw new UnsupportedOperationException("A GlobalVariable cannot have a parent"); 197 } 198 199 /** {@inheritDoc} */ 200 @Override 201 public boolean setParentForAllChildren(List<String> errors) { 202 throw new UnsupportedOperationException("A GlobalVariable cannot have a parent"); 203 } 204 205 /** {@inheritDoc} */ 206 @Override 207 public LogixNG getLogixNG() { 208 return null; 209 } 210 211 /** {@inheritDoc} */ 212 @Override 213 public ConditionalNG getConditionalNG() { 214 return null; 215 } 216 217 /** {@inheritDoc} */ 218 @Override 219 public final Base getRoot() { 220 return this; 221 } 222 223 /** {@inheritDoc} */ 224 @Override 225 public void registerListeners() { 226 } 227 228 /** {@inheritDoc} */ 229 @Override 230 public void unregisterListeners() { 231 } 232 233 /** {@inheritDoc} */ 234 @Override 235 public void printTree( 236 PrintTreeSettings settings, 237 PrintWriter writer, 238 String indent, 239 MutableInt lineNumber) { 240 241 printTree(settings, Locale.getDefault(), writer, indent, "", lineNumber); 242 } 243 244 /** {@inheritDoc} */ 245 @Override 246 public void printTree( 247 PrintTreeSettings settings, 248 Locale locale, 249 PrintWriter writer, 250 String indent, 251 MutableInt lineNumber) { 252 253 printTree(settings, locale, writer, indent, "", lineNumber); 254 } 255 256 /** {@inheritDoc} */ 257 @Override 258 public void printTree( 259 PrintTreeSettings settings, 260 Locale locale, 261 PrintWriter writer, 262 String indent, 263 String currentIndent, 264 MutableInt lineNumber) { 265 266 throw new UnsupportedOperationException("Not supported"); 267 } 268 269 @Override 270 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) { 271 throw new UnsupportedOperationException("Not supported yet."); 272 } 273 274 @Override 275 public Base deepCopyChildren(Base original, Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 276 throw new UnsupportedOperationException("Not supported"); 277 } 278 279 @Override 280 public void setup() { 281 throw new UnsupportedOperationException("Not supported"); 282 } 283 284 /** {@inheritDoc} */ 285 @Override 286 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 287 throw new UnsupportedOperationException("Not supported"); 288 } 289 290 /** {@inheritDoc} */ 291 @Override 292 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT", 293 justification="Specific log message format") 294 public void getUsageTree(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) { 295 log.debug("** {} :: {}", level, this.getClass().getName()); 296 297 throw new UnsupportedOperationException("Not supported"); 298 } 299 300 /** {@inheritDoc} */ 301 @Override 302 public void getUsageDetail(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) { 303 throw new UnsupportedOperationException("Not supported"); 304 } 305 306 /** {@inheritDoc} */ 307 @Override 308 public void getListenerRefsIncludingChildren(List<String> list) { 309 list.addAll(getListenerRefs()); 310 } 311 312 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultGlobalVariable.class); 313}