001package jmri.managers;
002
003import java.util.*;
004
005import javax.annotation.Nonnull;
006import javax.annotation.CheckForNull;
007
008import jmri.AddressedProgrammer;
009import jmri.AddressedProgrammerManager;
010import jmri.GlobalProgrammerManager;
011import jmri.Programmer;
012import jmri.ProgrammingMode;
013import jmri.beans.PropertyChangeSupport;
014import jmri.implementation.PermissionProgrammer;
015import jmri.implementation.PermissionAddressedProgrammer;
016
017import org.slf4j.Logger;
018import org.slf4j.LoggerFactory;
019
020/**
021 * Provides a very basic implementation of a programmer manager by providing a
022 * union of the AddressedProgrammerManager and GlobalProgrammerManager
023 * interfaces.
024 * <p>
025 * This implementation requires a service-mode Programmer at construction time
026 * and returns that Programmer for all global programming mode requests. This
027 * implementation of AddressedProgrammerManager always returns null for Op Mode,
028 * or addressed programmer requests, indicating there is no programmer of that
029 * type.
030 *
031 * @see jmri.AddressedProgrammerManager
032 * @see jmri.GlobalProgrammerManager
033 * @author Bob Jacobsen Copyright (C) 2001, 2015, 2016
034 */
035public class DefaultProgrammerManager extends PropertyChangeSupport implements AddressedProgrammerManager, GlobalProgrammerManager {
036
037    // For the record, these were the original numerical definitions:
038    //     public static final ProgrammingMode NONE              = new ProgrammingMode("NONE", 0);
039    //     public static final ProgrammingMode REGISTERMODE      = new ProgrammingMode("REGISTERMODE", 11);
040    //     public static final ProgrammingMode PAGEMODE          = new ProgrammingMode("PAGEMODE", 21);
041    //     public static final ProgrammingMode DIRECTBITMODE     = new ProgrammingMode("DIRECTBITMODE", 31);
042    //     public static final ProgrammingMode DIRECTBYTEMODE    = new ProgrammingMode("DIRECTBYTEMODE", 32);
043    //     public static final ProgrammingMode ADDRESSMODE       = new ProgrammingMode("ADDRESSMODE", 41);
044    //     public static final ProgrammingMode OPSBYTEMODE       = new ProgrammingMode("OPSBYTEMODE", 101);
045    //     public static final ProgrammingMode OPSBITMODE        = new ProgrammingMode("OPSBITMODE", 102);
046    //     public static final ProgrammingMode OPSACCBYTEMODE    = new ProgrammingMode("OPSACCBYTEMODE", 111);
047    //     public static final ProgrammingMode OPSACCBITMODE     = new ProgrammingMode("OPSACCBITMODE", 112);
048    //     public static final ProgrammingMode OPSACCEXTBYTEMODE = new ProgrammingMode("OPSACCEXTBYTEMODE", 121);
049    //     public static final ProgrammingMode OPSACCEXTBITMODE  = new ProgrammingMode("OPSACCEXTBITMODE", 122);
050    private Programmer programmer;
051    Map<Programmer, PermissionProgrammer> permissionProgrammers = new HashMap<>();
052    Map<AddressedProgrammer, PermissionAddressedProgrammer> permissionAddressedProgrammers = new HashMap<>();
053
054    /**
055     * Constructor when no global programmer is available.
056     */
057    public DefaultProgrammerManager() {
058        this(null);  // indicates not present
059    }
060
061    /**
062     * Constructor with a programmer.
063     *
064     * @param programmer the programmer to use; if null, acts as if no
065     *                   programmer is available
066     */
067    public DefaultProgrammerManager(@CheckForNull Programmer programmer) {
068        this.programmer = programmer;
069    }
070
071    /**
072     * Constructor with a programmer and associated connection.
073     *
074     * @param programmer the programmer to use; if null, acts as if no
075     *                   programmer is available
076     * @param memo       the associated connection
077     */
078    public DefaultProgrammerManager(@CheckForNull Programmer programmer, @Nonnull jmri.SystemConnectionMemo memo) {
079        this(programmer);
080        this.userName = memo.getUserName();
081    }
082
083    private String userName = "<Default>";
084
085    /**
086     * Provides the human-readable representation for including
087     * ProgrammerManagers directly in user interface components, so it should
088     * return a user-provided name for this particular one.
089     */
090    @Override
091    public String getUserName() {
092        return userName;
093    }
094
095    /**
096     * Provides the human-readable representation for including
097     * ProgrammerManagers directly in user interface components, so it should
098     * return a user-provided name for this particular one.
099     */
100    @Override
101    public String toString() {
102        return getUserName();
103    }
104
105    @Override
106    public final Programmer getGlobalProgrammer() {
107        Programmer p = getConcreteGlobalProgrammer();
108        if (p != null) {
109            return permissionProgrammers.computeIfAbsent(p,
110                    v -> new PermissionProgrammer(v));
111        } else {
112            return null;
113        }
114    }
115
116    /**
117     * Gain access to the Global Mode Programmer without reservation.
118     *
119     * @return null only if there isn't a Global Mode Programmer available via
120     *         this Manager.
121     */
122    @CheckForNull
123    protected Programmer getConcreteGlobalProgrammer() {
124        log.debug("return default service-mode programmer of type {}", (programmer != null ? programmer.getClass() : "(null)"));
125        return programmer;
126    }
127
128    @Override
129    public final AddressedProgrammer getAddressedProgrammer(boolean pLongAddress, int pAddress) {
130        AddressedProgrammer p = getConcreteAddressedProgrammer(pLongAddress, pAddress);
131        if (p != null) {
132            return permissionAddressedProgrammers.computeIfAbsent(p,
133                    v -> new PermissionAddressedProgrammer(v));
134        } else {
135            return null;
136        }
137    }
138
139    /**
140     * Gain access to a Addressed Mode Programmer without reservation.
141     *
142     * @param pLongAddress true if this is a long (14 bit) address, else false
143     * @param pAddress     specific decoder address to use
144     * @return null only if there isn't an Ops Mode Programmer in the system
145     */
146    @CheckForNull
147    protected AddressedProgrammer getConcreteAddressedProgrammer(boolean pLongAddress, int pAddress) {
148        return null;
149    }
150
151    @Override
152    public final Programmer reserveGlobalProgrammer() {
153        Programmer p = reserveConcreteGlobalProgrammer();
154        if (p != null) {
155            return permissionProgrammers.computeIfAbsent(p,
156                    v -> new PermissionProgrammer(v));
157        } else {
158            return null;
159        }
160    }
161
162    /**
163     * Gain access to the Global Mode Programmer, in the process reserving it
164     * for yourself.
165     *
166     * @return null if the existing Global Mode programmer is in use
167     */
168    protected Programmer reserveConcreteGlobalProgrammer() {
169        return programmer;
170    }
171
172    @Override
173    public void releaseGlobalProgrammer(@Nonnull Programmer p) {
174    }
175
176    @Override
177    public final AddressedProgrammer reserveAddressedProgrammer(boolean pLongAddress, int pAddress) {
178        AddressedProgrammer p = reserveConcreteAddressedProgrammer(pLongAddress, pAddress);
179        if (p != null) {
180            return permissionAddressedProgrammers.computeIfAbsent(p,
181                    v -> new PermissionAddressedProgrammer(v));
182        } else {
183            return null;
184        }
185    }
186
187    /**
188     * Gain access to a (the) Addressed Mode Programmer, in the process
189     * reserving it for yourself.
190     *
191     * @param pLongAddress true if this is a long (14 bit) address, else false
192     * @param pAddress     Specific decoder address to use
193     * @return null if the address is in use by a reserved programmer
194     */
195    @CheckForNull
196    protected AddressedProgrammer reserveConcreteAddressedProgrammer(boolean pLongAddress, int pAddress) {
197        return null;
198    }
199
200    @Override
201    public void releaseAddressedProgrammer(@Nonnull AddressedProgrammer p) {
202    }
203
204    /**
205     * {@inheritDoc}
206     *
207     * @return always false in this implementation
208     */
209    @Override
210    public boolean isAddressedModePossible() {
211        return false;
212    }
213
214    /**
215     * {@inheritDoc}
216     *
217     * @return always false in this implementation
218     */
219    @Override
220    public boolean isAddressedModePossible(@Nonnull jmri.LocoAddress l) {
221        return isAddressedModePossible();
222    }
223
224    /**
225     * {@inheritDoc}
226     *
227     * @return always false in this implementation
228     */
229    @Override
230    public boolean isGlobalProgrammerAvailable() {
231        return true;
232    }
233
234    /**
235     * {@inheritDoc}
236     *
237     * @return a default list of programming modes that most
238     *         {@link jmri.AddressedProgrammer}s make available
239     */
240    @Override
241    public List<ProgrammingMode> getDefaultModes() {
242        List<ProgrammingMode> retval = new java.util.ArrayList<>();
243        retval.add(ProgrammingMode.OPSBYTEMODE);
244        return retval;
245    }
246
247    public void dispose() {
248        if (programmer != null) {
249            programmer.dispose();
250        }
251    }
252
253    private final static Logger log = LoggerFactory.getLogger(DefaultProgrammerManager.class);
254}