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}