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}