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