/** * FormattedString.java * * A String that can include options of the form $optionName$. Option values * are maintained in a property list and getText returns the string will all * options replaced by their value * * */ package VASSAL.tools; import java.util.HashMap; import java.util.Map; import VASSAL.build.AbstractConfigurable; import VASSAL.build.BadDataReport; import VASSAL.build.GameModule; import VASSAL.build.module.properties.PropertySource; import VASSAL.counters.EditablePiece; import VASSAL.counters.GamePiece; import VASSAL.i18n.Resources; import VASSAL.script.expression.Expression; import VASSAL.script.expression.ExpressionException; public class FormattedString { // The actual string for display purposes protected String formatString; // An efficiently evaluable representation of the string protected Expression format; protected Map<String,String> props = new HashMap<String,String>(); protected PropertySource defaultProperties; public FormattedString() { this(""); } public FormattedString(String s) { this(s,GameModule.getGameModule()); } public FormattedString(PropertySource defaultProperties) { this("",defaultProperties); } public FormattedString(String formatString, PropertySource defaultProperties) { setFormat(formatString); this.defaultProperties = defaultProperties; } public void setFormat(String s) { formatString = s; format = Expression.createExpression(s); } public String getFormat() { return formatString; } public void setProperty(String name, String value) { props.put(name, value); } public void clearProperties() { props.clear(); } /** * Return the resulting string after substituting properties * @return */ public String getText() { return getText(defaultProperties, false); } public String getLocalizedText() { return getText(defaultProperties, true); } /** * Return the resulting string after substituting properties * Also, if any property keys match a property in the given GamePiece, * substitute the value of that property * @see GamePiece#getProperty * @param ps * @return */ public String getText(PropertySource ps) { return getText(ps, false); } /** * Return the resulting string after substituting properties * Also, if any property keys match a property in the given GamePiece, * substitute the value of that property. If the resulting string is * empty, then the default is returned. * @see GamePiece#getProperty * @param ps * @param def the default if the result is otherwise empty * @return */ public String getText(PropertySource ps, String def) { String s = getText(ps, false); if (s == null || s.length() == 0) { s = def; } return s; } public String getLocalizedText(PropertySource ps) { return getText(ps, true); } protected String getText(PropertySource ps, boolean localized){ final PropertySource source = (ps==null) ? defaultProperties : ps; try { return format.evaluate(source, props, localized); } catch (ExpressionException e) { if (source instanceof EditablePiece) { ErrorDialog.dataError(new BadDataReport((EditablePiece) source, Resources.getString("Error.expression_error"), format.getExpression(), e)); } else if (source instanceof AbstractConfigurable) { ErrorDialog.dataError(new BadDataReport((AbstractConfigurable) source, Resources.getString("Error.expression_error"), format.getExpression(), e)); } else { ErrorDialog.dataError(new BadDataReport(Resources.getString("Error.expression_error"), format.getExpression(), e)); } return ""; } } /** * Expand a FormattedString using the supplied propertySource and parse it as * an integer. If the expanded string is not an integer, generate a Bad Data Report * with debugging information and return a value of 0 * */ public int getTextAsInt(PropertySource ps, String description, EditablePiece source) { int result = 0; final String value = getText(ps, "0"); try { result = Integer.parseInt(value); } catch (NumberFormatException e) { ErrorDialog.dataError(new BadDataReport(source, Resources.getString("Error.non_number_error"), debugInfo(this, value, description), e)); } return result; } public int getTextAsInt(PropertySource ps, String description, AbstractConfigurable source) { int result = 0; final String value = getText(ps, "0"); try { result = Integer.parseInt(value); } catch (NumberFormatException e) { ErrorDialog.dataError(new BadDataReport(source, Resources.getString("Error.non_number_error"), debugInfo(this, value, description), e)); } return result; } /** * Format a standard debug message for use in Decorator bad data reports. * * description=value * description[format]=value * * Use format 1 if the generated value is the same as the format * Use format 2 if the formated contains an expression that has been expanded. * * @param format Formatted String * @param description Description of the String * @param value Value generated by the formatted string * @return error message */ public static String debugInfo(FormattedString format, String value, String description) { return description + (value.equals(format.getFormat()) ? "" : "[" + format.getFormat()+ "]") + "=" + value; } public String debugInfo(String value, String description) { return debugInfo(this, value, description); } public PropertySource getDefaultProperties() { return defaultProperties; } public void setDefaultProperties(PropertySource defaultProperties) { this.defaultProperties = defaultProperties; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((formatString == null) ? 0 : formatString.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; FormattedString other = (FormattedString) obj; if (formatString == null) { if (other.formatString != null) return false; } else if (!formatString.equals(other.formatString)) return false; return true; } }