001package jmri.jmrix.bidib; 002 003import java.util.ArrayList; 004import java.util.Locale; 005import javax.annotation.Nonnull; 006import jmri.JmriException; 007import jmri.Turnout; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * Implement turnout manager for BiDiB systems. 013 * <p> 014 * System names are "BTnnn", where B is the user configurable system prefix, 015 * nnn is the turnout number without padding. 016 * 017 * @author Bob Jacobsen Copyright (C) 2001 018 * @author Eckart Meyer Copyright (C) 2019-2023 019 */ 020public class BiDiBTurnoutManager extends jmri.managers.AbstractTurnoutManager {// implements EasyDccListener { 021 022 // Whether we accumulate partially loaded turnouts in pendingTurnouts. 023 private boolean isLoading = false; 024 // Turnouts that are being loaded from XML. 025 private final ArrayList<BiDiBTurnout> pendingTurnouts = new ArrayList<>(); 026 027 028 /** 029 * Create an new BiDiB TurnoutManager. 030 * 031 * @param memo the SystemConnectionMemo for this connection (contains the prefix string needed to parse names) 032 */ 033 public BiDiBTurnoutManager(BiDiBSystemConnectionMemo memo) { 034 super(memo); 035 } 036 037 /** 038 * {@inheritDoc} 039 */ 040 @Override 041 public BiDiBSystemConnectionMemo getMemo() { 042 return (BiDiBSystemConnectionMemo) memo; 043 } 044 045 /** 046 * Create a new Turnout based on the system name. 047 * Assumes calling method has checked that a Turnout with this 048 * system name does not already exist. 049 * 050 * @return null if the system name is not in a valid format. 051 */ 052 @Override 053 public Turnout createNewTurnout(String systemName, String userName) { 054 log.trace("createNewTurnout {} - {}", systemName, userName); 055 //String addr = systemName.substring(getSystemPrefix().length() + 1); 056 // first, check validity 057 try { 058 validateSystemNameFormat(systemName); 059 } catch (IllegalArgumentException e) { 060 log.error("failed to validate:", e); 061 throw e; 062 } 063 064 065 BiDiBTurnout t; 066 t = new BiDiBTurnout(systemName, this); 067 t.setUserName(userName); 068 069 synchronized (pendingTurnouts) { 070 if (isLoading) { 071 pendingTurnouts.add(t); 072 } else { 073 t.finishLoad(); 074 } 075 } 076 077 return t; 078 //return null; 079 } 080 081 /** 082 * This function is invoked before an XML load is started. We defer initialization of the 083 * newly created turnouts until finishLoad because the type might be changing as we 084 * are parsing the XML. 085 */ 086 public void startLoad() { 087 synchronized (pendingTurnouts) { 088 isLoading = true; 089 } 090 } 091 092 /** 093 * This function is invoked after the XML load is complete and all turnouts are instantiated 094 * and their config is read in. We use this hook to finalize the construction of the 095 * objects whose instantiation was deferred until the feedback type was known. 096 */ 097 public void finishLoad() { 098 log.info("Turnout manager : finish load"); 099 synchronized (pendingTurnouts) { 100 pendingTurnouts.forEach((t) -> { 101 t.finishLoad(); 102 }); 103 pendingTurnouts.clear(); 104 isLoading = false; 105 } 106 } 107 108 /** {@inheritDoc} */ 109 @Override 110 public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { //TODO some validation? Throw exception then? - see parent class 111 log.trace("createSystemName from {} - {}", curAddress, prefix); 112 try { 113 int i = 1; 114 int curNum = Integer.parseInt(curAddress); 115 for (Turnout t : getNamedBeanSet()) { 116 //log.trace("turnout: {}/{} {}", i, curNum, t.getSystemName()); 117 if (i++ == curNum) { 118 return t.getSystemName(); 119 } 120 } 121 } catch (java.lang.NumberFormatException ex) { 122 throw new JmriException("Hardware Address passed "+curAddress+" should be a number"); 123 } 124 return prefix + typeLetter() + curAddress; 125 } 126 127/* obsolete 128 /** {@inheritDoc} * / 129 @Override 130 public String getNextValidAddress(@Nonnull String curAddress, @Nonnull String prefix, boolean ignoreInitialExisting) { 131 log.trace("getNextValidAddress from {} - {}", curAddress, prefix); 132 // If the hardware address passed does not already exist then this can 133 // be considered the next valid address. 134 String tmpSName = ""; 135 try { 136 tmpSName = createSystemName(curAddress, prefix); 137 } catch (JmriException ex) { 138 jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class). 139 showErrorMessage(Bundle.getMessage("WarningTitle"), Bundle.getMessage("ErrorConvertNumberX", curAddress), null, "", true, false); 140 return null; 141 } 142 143 Turnout t = getBySystemName(tmpSName); 144 if (t == null && !ignoreInitialExisting) { 145 return curAddress; 146 } 147 return null; 148 } 149 */ 150 151 /** 152 * Public method to validate system name format. 153 * 154 * @param systemName system name 155 * @return VALID if system name has a valid format, else return INVALID 156 */ 157 @Override 158 public NameValidity validSystemNameFormat(String systemName) { 159 log.trace("validSystemNameFormat"); 160 // TODO!! 161 //return (getBitFromSystemName(systemName) != 0) ? NameValidity.VALID : NameValidity.INVALID; 162 return NameValidity.VALID; //TODO 163 } 164 165 /** 166 * {@inheritDoc} 167 */ 168 @Override 169 public String validateSystemNameFormat(String name, Locale locale) { 170 log.trace("validateSystemNameFormat: name: {}, typeLetter: {}", name, typeLetter()); 171 validateSystemNamePrefix(name, locale); 172 //validateAddressFormat(name.substring(getSystemNamePrefix().length())); 173 if (!BiDiBAddress.isValidSystemNameFormat(name, typeLetter(), getMemo())) { 174 throw new jmri.NamedBean.BadSystemNameException(Locale.getDefault(), "InvalidSystemName",name); 175 } 176 return name; 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override 183 public String getEntryToolTip() { 184 String entryToolTip = Bundle.getMessage("AddOutputEntryToolTip"); 185 return entryToolTip; 186 } 187 188 private final static Logger log = LoggerFactory.getLogger(BiDiBTurnoutManager.class); 189 190}