blob: 7ca45b14c2c81b4ca7ec99c064609bb752543a69 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.components;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import java.io.IOException;
import java.io.Writer;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Currency;
/**
* <!-- START SNIPPET: javadoc -->
* <p>
* Format Number object in different ways.
* </p>
*
* <p>
* The number tag will allow you to format a Number in a quick and easy way,
* based on the java.text.NumberFormat class. There are four basic number types,
* a number, a currency, a percentage and an integer. If a currency is
* specified, the number format will match the given currency. Further
* parameters can be overridden as needed.
* </p>
*
* <p>
* If a type is not defined, it will finally fall back to the default
* NumberFormat.getNumberInstance() formatting.
* </p>
*
* <p>
* <b>Note</b>: If the requested Number object isn't found on the stack, a blank
* will be returned.
* </p>
*
* <p>
* Configurable attributes are:
* </p>
*
* <ul>
* <li>name</li>
* <li>currency - you can specify your own currency or as an OGNL expression</li>
* <li>type - if not specified try to find base on struts.number.format property</li>
* <li>groupingUsed - see NumberFormat.isGroupingUsed</li>
* <li>maximumFractionDigits - see NumberFormat.setMaximumFractionDigits</li>
* <li>maximumIntegerDigits - see NumberFormat.setMaximumIntegerDigits</li>
* <li>minimumFractionDigits - see NumberFormat.setMinimumFractionDigits</li>
* <li>minimumIntegerDigits - see NumberFormat.setMinimumIntegerDigits</li>
* <li>parseIntegerOnly - see NumberFormat.isParseIntegerOnly</li>
* <li>roundingMode - see below</li>
* </ul>
*
*
* <p>
* Possible values for rounding mode are:
* </p>
*
* <ul>
* <li>ceiling</li>
* <li>down</li>
* <li>floor</li>
* <li>half-down</li>
* <li>half-even</li>
* <li>half-up</li>
* <li>unnecessary</li>
* <li>up</li>
* </ul>
*
* <!-- END SNIPPET: javadoc -->
*
* <p><b>Examples</b></p>
*
* <pre>
* &lt;!-- START SNIPPET: example --&gt;
* &lt;s:number name=&quot;invoice.total&quot; type=&quot;currency&quot; currency=&quot;XYZ&quot; /&gt;
* &lt;s:number name=&quot;invoice.quantity&quot; type=&quot;number&quot; /&gt;
* &lt;s:number name=&quot;invoice.discount&quot; type=&quot;percentage&quot; /&gt;
* &lt;s:number name=&quot;invoice.terms&quot; type=&quot;integer&quot; /&gt;
* &lt;!-- END SNIPPET: example --&gt;
* </pre>
*
* <p>
* <code>Number</code>
* </p>
*/
@StrutsTag(name = "number", tldBodyContent = "empty", tldTagClass = "org.apache.struts2.views.jsp.NumberTag", description = "Render a formatted number.")
public class Number extends ContextBean {
private static final Logger LOG = LogManager.getLogger(Number.class);
/**
* Property name to fall back when no format is specified
*/
public static final String NUMBERTAG_PROPERTY = "struts.number.format";
private String name;
private String currency;
private String type;
private Boolean groupingUsed;
private Integer maximumFractionDigits;
private Integer maximumIntegerDigits;
private Integer minimumFractionDigits;
private Integer minimumIntegerDigits;
private Boolean parseIntegerOnly;
private String roundingMode;
public Number(ValueStack stack) {
super(stack);
}
public boolean end(Writer writer, String body) {
java.lang.Number number = findNumberName();
if (number != null) {
NumberFormat format = getNumberFormat();
findCurrency(format);
setNumberFormatParameters(format);
setRoundingMode(format);
String msg = format.format(number);
try {
if (getVar() == null) {
writer.write(msg);
} else {
putInContext(msg);
}
} catch (IOException e) {
LOG.error("Could not write out Number tag", e);
}
}
return super.end(writer, "");
}
// try to find the currency, percentage and integer on the stack
private void findCurrency(NumberFormat format) {
if (currency != null) {
Object currencyValue = findValue(currency);
if (currencyValue != null) {
currency = currencyValue.toString();
}
try {
format.setCurrency(Currency.getInstance(currency));
} catch (IllegalArgumentException iae) {
LOG.error("Could not recognise a currency of [" + currency + "]");
}
}
}
private void setNumberFormatParameters(NumberFormat format) {
if (groupingUsed != null) {
format.setGroupingUsed(groupingUsed);
}
if (maximumFractionDigits != null) {
format.setMaximumFractionDigits(maximumFractionDigits);
}
if (maximumIntegerDigits != null) {
format.setMaximumIntegerDigits(maximumIntegerDigits);
}
if (minimumFractionDigits != null) {
format.setMinimumFractionDigits(minimumFractionDigits);
}
if (minimumIntegerDigits != null) {
format.setMinimumIntegerDigits(minimumIntegerDigits);
}
if (parseIntegerOnly != null) {
format.setParseIntegerOnly(parseIntegerOnly);
}
}
private java.lang.Number findNumberName() {
java.lang.Number number = null;
// find the name on the valueStack
try {
// suport Calendar also
Object numberObject = findValue(name);
if (numberObject instanceof java.lang.Number) {
number = (java.lang.Number) numberObject;
}
} catch (Exception e) {
LOG.error("Could not convert object with key [" + name + "] to a java.lang.Number instance");
}
return number;
}
private void setRoundingMode(NumberFormat format) {
if (roundingMode != null) {
roundingMode = findString(roundingMode);
if ("ceiling".equals(roundingMode)) {
format.setRoundingMode(RoundingMode.CEILING);
} else if ("down".equals(roundingMode)) {
format.setRoundingMode(RoundingMode.DOWN);
} else if ("floor".equals(roundingMode)) {
format.setRoundingMode(RoundingMode.FLOOR);
} else if ("half-down".equals(roundingMode)) {
format.setRoundingMode(RoundingMode.HALF_DOWN);
} else if ("half-even".equals(roundingMode)) {
format.setRoundingMode(RoundingMode.HALF_EVEN);
} else if ("half-up".equals(roundingMode)) {
format.setRoundingMode(RoundingMode.HALF_UP);
} else if ("unnecessary".equals(roundingMode)) {
format.setRoundingMode(RoundingMode.UNNECESSARY);
} else if ("up".equals(roundingMode)) {
format.setRoundingMode(RoundingMode.UP);
} else {
LOG.error("Could not recognise a roundingMode of [" + roundingMode + "]");
}
}
}
private NumberFormat getNumberFormat() {
NumberFormat format = null;
if (type == null) {
try {
type = findString(NUMBERTAG_PROPERTY);
} catch (Exception e) {
LOG.error("Could not find [" + NUMBERTAG_PROPERTY + "] on the stack!", e);
}
}
if (type != null) {
type = findString(type);
if ("currency".equals(type)) {
format = NumberFormat.getCurrencyInstance(ActionContext.getContext().getLocale());
} else if ("integer".equals(type)) {
format = NumberFormat.getIntegerInstance(ActionContext.getContext().getLocale());
} else if ("number".equals(type)) {
format = NumberFormat.getNumberInstance(ActionContext.getContext().getLocale());
} else if ("percent".equals(type)) {
format = NumberFormat.getPercentInstance(ActionContext.getContext().getLocale());
}
}
if (format == null) {
format = NumberFormat.getInstance(ActionContext.getContext().getLocale());
}
return format;
}
@StrutsTagAttribute(description = "Type of number formatter (currency, integer, number or percent, default is number)")
public void setType(String type) {
this.type = type;
}
@StrutsTagAttribute(description = "The currency to use for a currency format")
public void setCurrency(String currency) {
this.currency = currency;
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
@StrutsTagAttribute(description = "The number value to format", required = true)
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the format.
*/
public String getType() {
return type;
}
/**
* @return Returns the currency.
*/
public String getCurrency() {
return currency;
}
@StrutsTagAttribute(description = "Whether grouping is used", type = "Boolean")
public void setGroupingUsed(Boolean groupingUsed) {
this.groupingUsed = groupingUsed;
}
/**
* @return Returns the grouping used.
*/
public Boolean isGroupingUsed() {
return groupingUsed;
}
/**
* @return the maximumFractionDigits
*/
public Integer getMaximumFractionDigits() {
return maximumFractionDigits;
}
/**
* @param maximumFractionDigits the maximumFractionDigits to set
*/
@StrutsTagAttribute(description = "Maximum fraction digits", type = "Integer")
public void setMaximumFractionDigits(Integer maximumFractionDigits) {
this.maximumFractionDigits = maximumFractionDigits;
}
/**
* @return the maximumIntegerDigits
*/
public Integer getMaximumIntegerDigits() {
return maximumIntegerDigits;
}
/**
* @param maximumIntegerDigits the maximumIntegerDigits to set
*/
@StrutsTagAttribute(description = "Maximum integer digits", type = "Integer")
public void setMaximumIntegerDigits(Integer maximumIntegerDigits) {
this.maximumIntegerDigits = maximumIntegerDigits;
}
/**
* @return the minimumFractionDigits
*/
public Integer getMinimumFractionDigits() {
return minimumFractionDigits;
}
/**
* @param minimumFractionDigits the minimumFractionDigits to set
*/
@StrutsTagAttribute(description = "Minimum fraction digits", type = "Integer")
public void setMinimumFractionDigits(Integer minimumFractionDigits) {
this.minimumFractionDigits = minimumFractionDigits;
}
/**
* @return the minimumIntegerDigits
*/
public Integer getMinimumIntegerDigits() {
return minimumIntegerDigits;
}
/**
* @param minimumIntegerDigits the minimumIntegerDigits to set
*/
@StrutsTagAttribute(description = "Maximum integer digits", type = "Integer")
public void setMinimumIntegerDigits(Integer minimumIntegerDigits) {
this.minimumIntegerDigits = minimumIntegerDigits;
}
/**
* @return the parseIntegerOnly
*/
public Boolean isParseIntegerOnly() {
return parseIntegerOnly;
}
/**
* @param parseIntegerOnly the parseIntegerOnly to set
*/
@StrutsTagAttribute(description = "Parse integer only", type = "Boolean")
public void setParseIntegerOnly(Boolean parseIntegerOnly) {
this.parseIntegerOnly = parseIntegerOnly;
}
/**
* @return the roundingMode
*/
public String getRoundingMode() {
return roundingMode;
}
/**
* @param roundingMode the roundingMode to set
*/
@StrutsTagAttribute(description = "The rounding mode to use, possible values: ceiling, down, floor, half-down, half-even, half-up, unnecessary, up")
public void setRoundingMode(String roundingMode) {
this.roundingMode = roundingMode;
}
}