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}