001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.core;
054:
055: import java.io.IOException;
056: import java.text.NumberFormat;
057: import java.util.Locale;
058: import freemarker.template.*;
059:
060: /**
061: * An instruction that outputs the value of a numerical expression.
062: * @author <a href="mailto:jon@revusky.com">Jonathan Revusky</a>
063: */
064: final class NumericalOutput extends TemplateElement {
065:
066: private final Expression expression;
067: private final boolean hasFormat;
068: private final int minFracDigits;
069: private final int maxFracDigits;
070: private volatile FormatHolder formatCache; // creating new NumberFormat is slow operation
071:
072: NumericalOutput(Expression expression) {
073: this .expression = expression;
074: hasFormat = false;
075: this .minFracDigits = 0;
076: this .maxFracDigits = 0;
077: }
078:
079: NumericalOutput(Expression expression, int minFracDigits,
080: int maxFracDigits) {
081: this .expression = expression;
082: hasFormat = true;
083: this .minFracDigits = minFracDigits;
084: this .maxFracDigits = maxFracDigits;
085: }
086:
087: void accept(Environment env) throws TemplateException, IOException {
088: Number num = EvaluationUtil.getNumber(expression, env);
089:
090: FormatHolder fmth = formatCache; // atomic sampling
091: if (fmth == null || !fmth.locale.equals(env.getLocale())) {
092: synchronized (this ) {
093: fmth = formatCache;
094: if (fmth == null
095: || !fmth.locale.equals(env.getLocale())) {
096: NumberFormat fmt = NumberFormat
097: .getNumberInstance(env.getLocale());
098: if (hasFormat) {
099: fmt.setMinimumFractionDigits(minFracDigits);
100: fmt.setMaximumFractionDigits(maxFracDigits);
101: } else {
102: fmt.setMinimumFractionDigits(0);
103: fmt.setMaximumFractionDigits(50);
104: }
105: fmt.setGroupingUsed(false);
106: formatCache = new FormatHolder(fmt, env.getLocale());
107: fmth = formatCache;
108: }
109: }
110: }
111: // We must use Format even if hasFormat == false.
112: // Some locales may use non-Arabic digits, thus replacing the
113: // decimal separator in the result of toString() is not enough.
114: env.getOut().write(fmth.format.format(num));
115: }
116:
117: public String getCanonicalForm() {
118: StringBuffer buf = new StringBuffer("#{");
119: buf.append(expression.getCanonicalForm());
120: if (hasFormat) {
121: buf.append(" ; ");
122: buf.append("m");
123: buf.append(minFracDigits);
124: buf.append("M");
125: buf.append(maxFracDigits);
126: }
127: buf.append("}");
128: return buf.toString();
129: }
130:
131: public String getDescription() {
132: return getSource();
133: }
134:
135: boolean heedsOpeningWhitespace() {
136: return true;
137: }
138:
139: boolean heedsTrailingWhitespace() {
140: return true;
141: }
142:
143: private class FormatHolder {
144: final NumberFormat format;
145: final Locale locale;
146:
147: FormatHolder(NumberFormat format, Locale locale) {
148: this.format = format;
149: this.locale = locale;
150: }
151: }
152: }
|