001package jmri.jmrit.operations.rollingstock; 002 003import java.util.*; 004 005import javax.swing.JComboBox; 006 007import org.jdom2.Attribute; 008import org.jdom2.Element; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012import jmri.beans.PropertyChangeSupport; 013import jmri.jmrit.operations.OperationsPanel; 014import jmri.jmrit.operations.trains.TrainCommon; 015 016/** 017 * Represents an attribute a rolling stock can have. Some attributes are length, 018 * color, type, load, road, owner, model etc. 019 * 020 * @author Daniel Boudreau Copyright (C) 2014 021 * 022 */ 023public abstract class RollingStockAttribute extends PropertyChangeSupport { 024 025 protected static final int MIN_NAME_LENGTH = 1; 026 027 public RollingStockAttribute() { 028 } 029 030 public void dispose() { 031 list.clear(); 032 //TODO The removal of listeners causes the tests to fail. 033 // Need to reload all listeners for the tests to work. 034 // Only tests currently call dispose() 035 // remove all listeners 036// for (java.beans.PropertyChangeListener p : pcs.getPropertyChangeListeners()) 037// pcs.removePropertyChangeListener(p); 038 } 039 040 protected List<String> list = new ArrayList<>(); 041 042 public String[] getNames() { 043 if (list.isEmpty()) { 044 list.addAll(Arrays.asList(getDefaultNames().split(","))); 045 } 046 return list.toArray(new String[0]); 047 } 048 049 protected String getDefaultNames() { 050 return "Error"; // overridden // NOI18N 051 } 052 053 public void setNames(String[] names) { 054 if (names.length > 0) { 055 Arrays.sort(names); 056 for (String name : names) { 057 if (!list.contains(name)) { 058 list.add(name); 059 } 060 } 061 } 062 } 063 064 /** 065 * Performs number sort before adding to list 066 * @param lengths The set of strings to be ordered. 067 * 068 */ 069 public void setValues(String[] lengths) { 070 if (lengths.length == 0) { 071 return; 072 } 073 try { 074 jmri.util.StringUtil.numberSort(lengths); 075 } catch (NumberFormatException e) { 076 log.error("lengths are not all numeric, list:"); 077 for (int i = 0; i < lengths.length; i++) { 078 try { 079 Integer.parseInt(lengths[i]); 080 log.error("length {} = {}", i, lengths[i]); 081 } catch (NumberFormatException ee) { 082 log.error("length {} = {} is not a valid number!", i, lengths[i]); 083 } 084 } 085 } 086 for (String length : lengths) { 087 if (!list.contains(length)) { 088 list.add(length); 089 } 090 } 091 } 092 093 public void sort() { 094 java.util.Collections.sort(list); 095 } 096 097 public void addName(String name) { 098 if (name == null) { 099 return; 100 } 101 if (list.contains(name)) { 102 return; 103 } 104 list.add(name); 105 sort(); 106 maxNameLength = 0; // reset maximum name length 107 maxNameSubStringLength = 0; 108 } 109 110 public void deleteName(String name) { 111 list.remove(name); 112 maxNameLength = 0; // reset maximum name length 113 maxNameSubStringLength = 0; 114 } 115 116 public boolean containsName(String name) { 117 return list.contains(name); 118 } 119 120 public JComboBox<String> getComboBox() { 121 JComboBox<String> box = new JComboBox<>(); 122 updateComboBox(box); 123 OperationsPanel.padComboBox(box); 124 return box; 125 } 126 127 public void updateComboBox(JComboBox<String> box) { 128 box.removeAllItems(); 129 for (String name : getNames()) { 130 box.addItem(name); 131 } 132 } 133 134 protected String maxName = ""; 135 protected int maxNameLength = 0; 136 137 public int getMaxNameLength() { 138 if (maxNameLength == 0) { 139 maxName = ""; 140 maxNameLength = getMinNameLength(); 141 for (String name : getNames()) { 142 if (name.length() > maxNameLength) { 143 maxName = name; 144 maxNameLength = name.length(); 145 } 146 } 147 } 148 return maxNameLength; 149 } 150 151 protected int maxNameSubStringLength = 0; 152 153 public int getMaxNameSubStringLength() { 154 if (maxNameSubStringLength == 0) { 155 maxName = ""; 156 maxNameSubStringLength = getMinNameLength(); 157 for (String name : getNames()) { 158 String[] subString = name.split(TrainCommon.HYPHEN); 159 if (subString.length > 0 && subString[0].length() > maxNameSubStringLength) { 160 maxName = name; 161 maxNameSubStringLength = subString[0].length(); 162 } 163 } 164 } 165 return maxNameSubStringLength; 166 } 167 168 protected int getMinNameLength() { 169 return MIN_NAME_LENGTH; 170 } 171 172 /** 173 * Create an XML element to represent this Entry. This member has to remain 174 * synchronized with the detailed DTD in operations-cars.dtd and operations-engines.dtd. 175 * @param root Common Element for storage. 176 * @param eNames New format Element group name 177 * @param eName New format Element name 178 * 179 */ 180 public void store(Element root, String eNames, String eName) { 181 Element names = new Element(eNames); 182 for (String name : getNames()) { 183 Element e = new Element(eName); 184 if (eName.equals(Xml.LENGTH)) { 185 e.setAttribute(new Attribute(Xml.VALUE, name)); 186 } else { 187 e.setAttribute(new Attribute(Xml.NAME, name)); 188 } 189 names.addContent(e); 190 } 191 root.addContent(names); 192 } 193 194 public void load(Element root, String eNames, String eName, String oldName) { 195 // new format using elements starting version 3.3.1 196 if (root.getChild(eNames) != null) { 197 List<Element> l = root.getChild(eNames).getChildren(eName); 198 Attribute a; 199 String[] names = new String[l.size()]; 200 for (int i = 0; i < l.size(); i++) { 201 Element name = l.get(i); 202 if ((a = name.getAttribute(Xml.NAME)) != null) { 203 names[i] = a.getValue(); 204 } 205 // lengths use "VALUE" 206 if ((a = name.getAttribute(Xml.VALUE)) != null) { 207 names[i] = a.getValue(); 208 } 209 210 } 211 setNames(names); 212 } // try old format 213 else if (root.getChild(oldName) != null) { 214 String[] names = root.getChildText(oldName).split("%%"); // NOI18N 215 setNames(names); 216 } 217 } 218 219 private final static Logger log = LoggerFactory.getLogger(RollingStockAttribute.class); 220 221}