001package jmri.jmrit.operations.rollingstock; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.List; 006 007import javax.swing.JComboBox; 008 009import org.jdom2.Attribute; 010import org.jdom2.Element; 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014import jmri.beans.PropertyChangeSupport; 015import jmri.jmrit.operations.trains.TrainCommon; 016 017/** 018 * Represents an attribute a rolling stock can have. Some attributes are length, 019 * color, type, load, road, owner, model etc. 020 * 021 * @author Daniel Boudreau Copyright (C) 2014 022 * 023 */ 024public abstract class RollingStockAttribute extends PropertyChangeSupport { 025 026 protected static final int MIN_NAME_LENGTH = 1; 027 028 public RollingStockAttribute() { 029 } 030 031 public void dispose() { 032 list.clear(); 033 //TODO The removal of listeners causes the tests to fail. 034 // Need to reload all listeners for the tests to work. 035 // Only tests currently call dispose() 036 // remove all listeners 037// for (java.beans.PropertyChangeListener p : pcs.getPropertyChangeListeners()) 038// pcs.removePropertyChangeListener(p); 039 } 040 041 protected List<String> list = new ArrayList<>(); 042 043 public String[] getNames() { 044 if (list.isEmpty()) { 045 list.addAll(Arrays.asList(getDefaultNames().split(","))); 046 } 047 return list.toArray(new String[0]); 048 } 049 050 protected String getDefaultNames() { 051 return "Error"; // overridden // NOI18N 052 } 053 054 public void setNames(String[] names) { 055 if (names.length > 0) { 056 Arrays.sort(names); 057 for (String name : names) { 058 if (!list.contains(name)) { 059 list.add(name); 060 } 061 } 062 } 063 } 064 065 /** 066 * Performs number sort before adding to list 067 * @param lengths The set of strings to be ordered. 068 * 069 */ 070 public void setValues(String[] lengths) { 071 if (lengths.length == 0) { 072 return; 073 } 074 try { 075 jmri.util.StringUtil.numberSort(lengths); 076 } catch (NumberFormatException e) { 077 log.error("lengths are not all numeric, list:"); 078 for (int i = 0; i < lengths.length; i++) { 079 try { 080 Integer.parseInt(lengths[i]); 081 log.error("length {} = {}", i, lengths[i]); 082 } catch (NumberFormatException ee) { 083 log.error("length {} = {} is not a valid number!", i, lengths[i]); 084 } 085 } 086 } 087 for (String length : lengths) { 088 if (!list.contains(length)) { 089 list.add(length); 090 } 091 } 092 } 093 094 public void sort() { 095 java.util.Collections.sort(list); 096 } 097 098 public void addName(String name) { 099 if (name == null) { 100 return; 101 } 102 if (list.contains(name)) { 103 return; 104 } 105 list.add(name); 106 sort(); 107 maxNameLength = 0; // reset maximum name length 108 maxNameSubStringLength = 0; 109 } 110 111 public void deleteName(String name) { 112 list.remove(name); 113 maxNameLength = 0; // reset maximum name length 114 maxNameSubStringLength = 0; 115 } 116 117 public boolean containsName(String name) { 118 return list.contains(name); 119 } 120 121 public JComboBox<String> getComboBox() { 122 JComboBox<String> box = new JComboBox<>(); 123 updateComboBox(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}