001package jmri.jmrit.logixng.implementation; 002 003import java.io.PrintWriter; 004import java.util.*; 005 006import javax.annotation.CheckReturnValue; 007import javax.annotation.Nonnull; 008 009import jmri.*; 010import jmri.implementation.AbstractNamedBean; 011import jmri.jmrit.logixng.*; 012 013import org.apache.commons.lang3.mutable.MutableInt; 014import org.slf4j.Logger; 015 016/** 017 * The abstract class that is the base class for all LogixNG classes that 018 * implements the Base interface. 019 */ 020public abstract class AbstractBase 021 extends AbstractNamedBean 022 implements Base { 023 024 private final Category _category; 025 protected boolean _listenersAreRegistered = false; 026 027 public AbstractBase(String sys) throws BadSystemNameException { 028 super(sys); 029 _category = Category.ITEM; 030 } 031 032 public AbstractBase(String sys, String user) 033 throws BadUserNameException, BadSystemNameException { 034 super(sys, user); 035 _category = Category.ITEM; 036 } 037 038 public AbstractBase(String sys, Category category) throws BadSystemNameException { 039 super(sys); 040 _category = category; 041 } 042 043 public AbstractBase(String sys, String user, Category category) 044 throws BadUserNameException, BadSystemNameException { 045 super(sys, user); 046 _category = category; 047 } 048 049 /** {@inheritDoc} */ 050 @Override 051 public Category getCategory() { 052 return _category; 053 } 054 055 /** {@inheritDoc} */ 056 @Override 057 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 058 // Default implementation is to throw UnsupportedOperationException. 059 // Classes that have children must override this method. 060 throw new UnsupportedOperationException("Not supported."); 061 } 062 063 /** {@inheritDoc} */ 064 @Override 065 public int getChildCount() { 066 // Default implementation is to return 0 children. 067 // Classes that have children must override this method. 068 return 0; 069 } 070 071 /** {@inheritDoc} */ 072 @Override 073 public Base deepCopyChildren(Base original, Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 074 for (int i=0; i < original.getChildCount(); i++) { 075 // Copy the name of the socket. 076 // Ignore duplicate errors since these errors might happen temporary in this loop. 077 getChild(i).setName(original.getChild(i).getName(), true); 078 079 // Copy the child 080 if (original.getChild(i).isConnected()) { 081 Base childTree = original.getChild(i).getConnectedSocket().getDeepCopy(systemNames, userNames); 082 getChild(i).connect((MaleSocket) childTree); 083 } 084 } 085 return this; 086 } 087 088 /** {@inheritDoc} */ 089 @Override 090 public ConditionalNG getConditionalNG() { 091 if (this instanceof ConditionalNG) return (ConditionalNG)this; 092 if (getParent() == null) return null; 093 return getParent().getConditionalNG(); 094 } 095 096 /** {@inheritDoc} */ 097 @Override 098 public final LogixNG getLogixNG() { 099 if (this instanceof LogixNG) return (LogixNG)this; 100 if (getParent() == null) return null; 101 return getParent().getLogixNG(); 102 } 103 104 /** {@inheritDoc} */ 105 @Override 106 public final Base getRoot() { 107 Base item = this; 108 while (item.getParent() != null) { 109 item = item.getParent(); 110 } 111 return item; 112 } 113 114 /** {@inheritDoc} */ 115 @Override 116 public final boolean setParentForAllChildren(List<String> errors) { 117 boolean result = true; 118 for (int i=0; i < getChildCount(); i++) { 119 FemaleSocket femaleSocket = getChild(i); 120 if (femaleSocket.isConnected()) { 121 MaleSocket connectedSocket = femaleSocket.getConnectedSocket(); 122 if ((connectedSocket.getParent() != null) 123 && (connectedSocket.getParent() != femaleSocket)) { 124 errors.add(Bundle.getMessage("DuplicateParentMessage", 125 connectedSocket.getSystemName(), 126 connectedSocket.getParent().getSystemName(), 127 getSystemName())); 128 log.error("The child {} already has the parent {} so it cannot be added to {}", 129 connectedSocket.getSystemName(), 130 connectedSocket.getParent().getSystemName(), 131 getSystemName()); 132 femaleSocket.disconnect(); 133 result = false; 134 } else { 135 connectedSocket.setParent(femaleSocket); 136 result = result && connectedSocket.setParentForAllChildren(errors); 137 } 138 } 139 } 140 return result; 141 } 142 143 /** 144 * Register listeners if this object needs that. 145 * <P> 146 * Important: This method may be called more than once. Methods overriding 147 * this method must ensure that listeners are not registered more than once. 148 */ 149 protected void registerListenersForThisClass() { 150 // Do nothing 151 } 152 153 /** 154 * Unregister listeners if this object needs that. 155 * <P> 156 * Important: This method may be called more than once. Methods overriding 157 * this method must ensure that listeners are not unregistered more than once. 158 */ 159 protected void unregisterListenersForThisClass() { 160 // Do nothing 161 } 162 163 /** {@inheritDoc} */ 164 @Override 165 public final void registerListeners() { 166 if (isActive()) { 167 registerListenersForThisClass(); 168 for (int i=0; i < getChildCount(); i++) { 169 getChild(i).registerListeners(); 170 } 171 } 172 } 173 174 /** {@inheritDoc} */ 175 @Override 176 public final void unregisterListeners() { 177 unregisterListenersForThisClass(); 178 for (int i=0; i < getChildCount(); i++) { 179 getChild(i).unregisterListeners(); 180 } 181 } 182 183 /** {@inheritDoc} */ 184 @Override 185 public final boolean isActive() { 186 return isEnabled() && ((getParent() == null) || getParent().isActive()); 187 } 188 189 protected void printTreeRow( 190 PrintTreeSettings settings, 191 Locale locale, 192 PrintWriter writer, 193 String currentIndent, 194 MutableInt lineNumber) { 195 196 if (settings._printLineNumbers) { 197 writer.append(String.format(PRINT_LINE_NUMBERS_FORMAT, lineNumber.addAndGet(1))); 198 } 199 writer.append(currentIndent); 200 writer.append(getLongDescription(locale)); 201 if (settings._printDisabled && !isEnabled()) { 202 writer.append(" ::: ").append(Bundle.getMessage("Disabled")); 203 } 204 if (settings._printStartup && (this instanceof ConditionalNG) && (((ConditionalNG)this).isExecuteAtStartup())) { 205 writer.append(" ::: ").append(Bundle.getMessage("Startup")); 206 } 207 writer.println(); 208 } 209 210 /** {@inheritDoc} */ 211 @Override 212 public void printTree( 213 PrintTreeSettings settings, 214 PrintWriter writer, 215 String indent, 216 MutableInt lineNumber) { 217 218 printTree(settings, Locale.getDefault(), writer, indent, "", lineNumber); 219 } 220 221 /** {@inheritDoc} */ 222 @Override 223 public void printTree( 224 PrintTreeSettings settings, 225 Locale locale, 226 PrintWriter writer, 227 String indent, 228 MutableInt lineNumber) { 229 230 printTree(settings, locale, writer, indent, "", lineNumber); 231 } 232 233 /** {@inheritDoc} */ 234 @Override 235 public void printTree( 236 PrintTreeSettings settings, 237 Locale locale, 238 PrintWriter writer, 239 String indent, 240 String currentIndent, 241 MutableInt lineNumber) { 242 243 printTreeRow(settings, locale, writer, currentIndent, lineNumber); 244 245 for (int i=0; i < getChildCount(); i++) { 246 getChild(i).printTree(settings, locale, writer, indent, currentIndent+indent, lineNumber); 247 } 248 } 249 250 /** {@inheritDoc} */ 251 @Override 252 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT", 253 justification="Specific log message format") 254 public void getUsageTree(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) { 255 log.debug("## {} :: {}", level, this.getLongDescription()); 256 level++; 257 for (int i=0; i < getChildCount(); i++) { 258 getChild(i).getUsageTree(level, bean, report, cdl); 259 } 260 } 261 262 /** {@inheritDoc} */ 263 @Override 264 public void getUsageDetail(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) { 265 } 266 267 /** 268 * {@inheritDoc} 269 * 270 * Do a string comparison. 271 */ 272 @CheckReturnValue 273 @Override 274 public int compareSystemNameSuffix(@Nonnull String suffix1, @Nonnull String suffix2, @Nonnull NamedBean n) { 275 return suffix1.compareTo(suffix2); 276 } 277 278 /** 279 * Dispose this class. 280 * Listeners do not need to be unregistered by this method since they are 281 * unregistered by dispose(). 282 */ 283 protected void disposeMe() { 284 // Do nothing 285 } 286 287 /** {@inheritDoc} */ 288 @Override 289 public final void dispose() { 290 super.dispose(); 291 for (int i=0; i < getChildCount(); i++) { 292 getChild(i).dispose(); 293 } 294 unregisterListeners(); 295 disposeMe(); 296 } 297 298 public void assertListenersAreNotRegistered(Logger log, String method) { 299 if (_listenersAreRegistered) { 300 RuntimeException e = new RuntimeException(method + " must not be called when listeners are registered"); 301 log.error("{} must not be called when listeners are registered", method, e); 302 throw e; 303 } 304 } 305 306 /** {@inheritDoc} */ 307 @Override 308 public void getListenerRefsIncludingChildren(List<String> list) { 309 list.addAll(getListenerRefs()); 310 for (int i=0; i < getChildCount(); i++) { 311 getChild(i).getListenerRefsIncludingChildren(list); 312 } 313 } 314 315 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractBase.class); 316}