| /* |
| * 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> |
| * <!-- START SNIPPET: example --> |
| * <s:number name="invoice.total" type="currency" currency="XYZ" /> |
| * <s:number name="invoice.quantity" type="number" /> |
| * <s:number name="invoice.discount" type="percentage" /> |
| * <s:number name="invoice.terms" type="integer" /> |
| * <!-- END SNIPPET: example --> |
| * </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; |
| } |
| |
| } |