001package jmri;
002
003import java.beans.PropertyVetoException;
004import javax.annotation.Nonnull;
005import jmri.beans.ConstrainedBean;
006
007/**
008 * Define the characteristics of a layout scale.  A scale has four properties.
009 * <ul>
010 * <li>Name - A fixed string, such N or HO.
011 * <li>User name - An alternate name that can be changed.  It defaults to the scale name.
012 * <li>Ratio - The ratio for the scale, such as 160 for N scale.
013 * <li>Factor - A derived value created by dividing 1 by the scale ratio.
014 * </ul>
015 * In addition to the standard scales, there is custom entry.  Custom settings
016 * are retained in a local copy of ScaleData.xml.
017 * <p>
018 * Methods are provided to set/get the user name and the scale ratio.  The scale
019 * factor is generated from the scale ratio and is read only, as is the scale name.
020 * <p>
021 * While changing the ratio and user names of the standard scales is not
022 * prohibited, doing so is not recommended due to potential conflicts with other
023 * applications.
024 * <p>
025 * Changes to user names and ratios send a <strong>vetoableChange</strong> event.
026 * Interested applications can add a <strong>vetoableChange</strong> listener
027 * in order to be notified when an event occurs. If the listener determines that
028 * the change cannot occur, it can throw a <strong>PropertyVetoException</strong>.
029 * <p>
030 * See {@link jmri.ScaleManager Scale Manager} for manager details.
031 *
032 * @author Dave Sand Copyright (C) 2018
033 * @since 4.13.6
034 */
035public class Scale extends ConstrainedBean {
036
037    public Scale() {
038        super();
039    }
040
041    public Scale(@Nonnull String name, double ratio, String userName) {
042        super();
043        _name = name;
044        _userName = (userName == null) ? name : userName;
045        _ratio = ratio;
046        _factor = 1.0 / _ratio;
047    }
048
049    private String _name = "HO";  // NOI18N
050    private String _userName = "HO";  // NOI18N
051    private double _ratio = 87.1;
052    private double _factor = 1 / 87.1;
053
054    public String getScaleName() {
055        return _name;
056    }
057
058    public String getUserName() {
059        return _userName;
060    }
061
062    public double getScaleRatio() {
063        return _ratio;
064    }
065
066    public double getScaleFactor() {
067        return _factor;
068    }
069
070    /**
071     * Set the user name for the current scale.
072     * Registered listeners can veto the change.
073     * @param newName The name to be applied if unique.
074     * @throws IllegalArgumentException The supplied name is a duplicate.
075     * @throws PropertyVetoException The user name change was vetoed.
076     */
077    public void setUserName(@Nonnull String newName) throws IllegalArgumentException, PropertyVetoException {
078        for (Scale scale : ScaleManager.getScales()) {
079            if (scale.getUserName().equals(newName)) {
080                if (!scale.getScaleName().equals(_name)) {
081                    throw new IllegalArgumentException("Duplicate scale user name");  // NOI18N
082                }
083            }
084        }
085
086        String oldName = _userName;
087        _userName = newName;
088
089        try {
090            fireVetoableChange("ScaleUserName", oldName, newName);  // NOI18N
091        } catch (PropertyVetoException ex) {
092            // Roll back change
093            log.warn("The user name change for {} scale to {} was rejected: Reason: {}",  // NOI18N
094                     _name, _userName, ex.getMessage());
095            _userName = oldName;
096            throw ex;  // Notify caller
097        }
098        jmri.configurexml.ScaleConfigXML.doStore();
099    }
100
101    /**
102     * Set the new scale ratio and calculate the scale factor.
103     * Registered listeners can veto the change.
104     * @param newRatio A double value containing the ratio.
105     * @throws IllegalArgumentException The new ratio is less than 1.
106     * @throws PropertyVetoException The ratio change was vetoed.
107     */
108    public void setScaleRatio(double newRatio) throws IllegalArgumentException, PropertyVetoException {
109        if (newRatio < 1.0) {
110            throw new IllegalArgumentException("The scale ratio is less than 1");  // NOI18N
111        }
112
113        double oldRatio = _ratio;
114        _ratio = newRatio;
115        _factor = 1.0 / _ratio;
116
117        try {
118            fireVetoableChange("ScaleRatio", oldRatio, newRatio);  // NOI18N
119        } catch (PropertyVetoException ex) {
120            // Roll back change
121            log.warn("The ratio change for {} scale to {} was rejected: Reason: {}",  // NOI18N
122                     _name, _ratio, ex.getMessage());
123            _ratio = oldRatio;
124            _factor = 1.0 / oldRatio;
125            throw ex;  // Notify caller
126        }
127        jmri.configurexml.ScaleConfigXML.doStore();
128    }
129
130    @Override
131    public String toString() {
132        return String.format("%s (%.1f)", getUserName(), getScaleRatio());
133    }
134
135    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Scale.class);
136
137}