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 writer.println(); 202 } 203 204 /** {@inheritDoc} */ 205 @Override 206 public void printTree( 207 PrintTreeSettings settings, 208 PrintWriter writer, 209 String indent, 210 MutableInt lineNumber) { 211 212 printTree(settings, Locale.getDefault(), writer, indent, "", lineNumber); 213 } 214 215 /** {@inheritDoc} */ 216 @Override 217 public void printTree( 218 PrintTreeSettings settings, 219 Locale locale, 220 PrintWriter writer, 221 String indent, 222 MutableInt lineNumber) { 223 224 printTree(settings, locale, writer, indent, "", lineNumber); 225 } 226 227 /** {@inheritDoc} */ 228 @Override 229 public void printTree( 230 PrintTreeSettings settings, 231 Locale locale, 232 PrintWriter writer, 233 String indent, 234 String currentIndent, 235 MutableInt lineNumber) { 236 237 printTreeRow(settings, locale, writer, currentIndent, lineNumber); 238 239 for (int i=0; i < getChildCount(); i++) { 240 getChild(i).printTree(settings, locale, writer, indent, currentIndent+indent, lineNumber); 241 } 242 } 243 244 /** {@inheritDoc} */ 245 @Override 246 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT", 247 justification="Specific log message format") 248 public void getUsageTree(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) { 249 log.debug("## {} :: {}", level, this.getLongDescription()); 250 level++; 251 for (int i=0; i < getChildCount(); i++) { 252 getChild(i).getUsageTree(level, bean, report, cdl); 253 } 254 } 255 256 /** {@inheritDoc} */ 257 @Override 258 public void getUsageDetail(int level, NamedBean bean, List<jmri.NamedBeanUsageReport> report, NamedBean cdl) { 259 } 260 261 /** 262 * {@inheritDoc} 263 * 264 * Do a string comparison. 265 */ 266 @CheckReturnValue 267 @Override 268 public int compareSystemNameSuffix(@Nonnull String suffix1, @Nonnull String suffix2, @Nonnull NamedBean n) { 269 return suffix1.compareTo(suffix2); 270 } 271 272 /** 273 * Dispose this class. 274 * Listeners do not need to be unregistered by this method since they are 275 * unregistered by dispose(). 276 */ 277 protected void disposeMe() { 278 // Do nothing 279 } 280 281 /** {@inheritDoc} */ 282 @Override 283 public final void dispose() { 284 super.dispose(); 285 for (int i=0; i < getChildCount(); i++) { 286 getChild(i).dispose(); 287 } 288 unregisterListeners(); 289 disposeMe(); 290 } 291 292 public void assertListenersAreNotRegistered(Logger log, String method) { 293 if (_listenersAreRegistered) { 294 RuntimeException e = new RuntimeException(method + " must not be called when listeners are registered"); 295 log.error("{} must not be called when listeners are registered", method, e); 296 throw e; 297 } 298 } 299 300 /** {@inheritDoc} */ 301 @Override 302 public void getListenerRefsIncludingChildren(List<String> list) { 303 list.addAll(getListenerRefs()); 304 for (int i=0; i < getChildCount(); i++) { 305 getChild(i).getListenerRefsIncludingChildren(list); 306 } 307 } 308 309 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractBase.class); 310}