001package jmri.managers;
002
003import javax.annotation.Nonnull;
004
005import jmri.*;
006
007/**
008 * Implementation of a Manager that can serves as a proxy for multiple
009 * system-specific implementations.
010 * <p>
011 * Automatically includes an Internal system, which need not be separately added
012 * any more.
013 * <p>
014 * Encapsulates access to the "Primary" manager, used by default, which is the
015 * first one provided.
016 * <p>
017 * Internally, this is done by using an ordered list of all non-Internal
018 * managers, plus a separate reference to the internal manager and default
019 * manager.
020 *
021 * @param <E> the supported type of NamedBean
022 * @author Bob Jacobsen      Copyright (C) 2003, 2010, 2018
023 * @author Daniel Bergqvist  Copyright (C) 2020
024 */
025abstract public class AbstractProvidingProxyManager<E extends NamedBean> extends AbstractProxyManager<E> implements ProvidingManager<E> {
026
027    /**
028     * Locate via user name, then system name if needed. If that fails, create a
029     * new NamedBean: If the name is a valid system name, it will be used for
030     * the new NamedBean. Otherwise, the makeSystemName method will attempt to
031     * turn it into a valid system name. Subclasses use this to create provider methods such as
032     * getSensor or getTurnout via casts.
033     *
034     * @param name the user name or system name of the bean
035     * @return an existing or new NamedBean
036     * @throws IllegalArgumentException if name is not usable in a bean
037     */
038    @Nonnull
039    protected E provideNamedBean(String name) throws IllegalArgumentException {
040        // make sure internal present
041        initInternal();
042
043        E t = getNamedBean(name);
044        if (t != null) {
045            return t;
046        }
047        // Doesn't exist. If the systemName was specified, find that system
048        Manager<E> manager = getManager(name);
049        if (manager != null) {
050            return makeBean(manager, name, null);
051        }
052        log.debug("provideNamedBean did not find manager for name {}, defer to default", name); // NOI18N
053        return makeBean(getDefaultManager(), getDefaultManager().makeSystemName(name), null);
054    }
055
056    /**
057     * Return an instance with the specified user or system name. 
058     * <p>
059     * Lookup by UserName, then provide by System Name.
060     * <p>
061     * Note that
062     * two calls with the same arguments will get the same instance; there is
063     * i.e. only one Sensor object representing a given physical sensor and
064     * therefore only one with a specific system or user name.
065     * <p>
066     * This will always return a valid object reference for a valid request; a
067     * new object will be created if necessary. In that case:
068     * <ul>
069     * <li>If a null reference is given for user name, no user name will be
070     * associated with the NamedBean object created; a valid system name must be
071     * provided
072     * <li>If both names are provided, the system name defines the hardware
073     * access of the desired turnout, and the user address is associated with
074     * it.
075     * <li>If a matching UserName is located, that will be returned.
076     * <li>Else If a matching SystemName is located, that will be returned.
077     * <li>Else A New Bean will be created with the given System Name.
078     * The UserName will be added to the New Bean if no existing.
079     * </ul>
080     * Note that it is possible to make an inconsistent request if both
081     * addresses are provided, but the given values are associated with
082     * different objects. This is a problem, and we don't have a good solution
083     * except to issue warnings. This will mostly happen if you're creating
084     * NamedBean when you should be looking them up.
085     * <p>
086     * If the System Name contains the start of a specified Manager, that will be used,
087     * else the default manager will be used.
088     * @see #getManager(java.lang.String)
089     *
090     * @param systemName the system name
091     * @param userName   the user name
092     * @return requested NamedBean object (never null)
093     */
094    @Nonnull
095    public E newNamedBean(@Nonnull String systemName, String userName) throws IllegalArgumentException {
096        // make sure internal present
097        initInternal();
098
099        // if the systemName is specified, find that system
100        Manager<E> m = getManager(systemName);
101        if (m != null) {
102            return makeBean(m, systemName, userName);
103        }
104
105        // did not find a manager, allow it to default to the primary
106        log.debug("Did not find manager for system name {}, delegate to primary", systemName); // NOI18N
107        return makeBean(getDefaultManager(), systemName, userName);
108    }
109
110    /**
111     * Defer creation of the proper type to the subclass.
112     *
113     * @param manager    the manager to invoke
114     * @param systemName the system name
115     * @param userName   the user name
116     * @throws IllegalArgumentException if unable to make.
117     * @return a bean
118     */
119    @Nonnull
120    abstract protected E makeBean(Manager<E> manager,@Nonnull String systemName, String userName) throws IllegalArgumentException;
121
122    // initialize logging
123    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractProvidingProxyManager.class);
124
125}