0001: /* ====================================================================
0002: * Tea - Copyright (c) 1997-2000 Walt Disney Internet Group
0003: * ====================================================================
0004: * The Tea Software License, Version 1.1
0005: *
0006: * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
0007: *
0008: * Redistribution and use in source and binary forms, with or without
0009: * modification, are permitted provided that the following conditions
0010: * are met:
0011: *
0012: * 1. Redistributions of source code must retain the above copyright
0013: * notice, this list of conditions and the following disclaimer.
0014: *
0015: * 2. Redistributions in binary form must reproduce the above copyright
0016: * notice, this list of conditions and the following disclaimer in
0017: * the documentation and/or other materials provided with the
0018: * distribution.
0019: *
0020: * 3. The end-user documentation included with the redistribution,
0021: * if any, must include the following acknowledgment:
0022: * "This product includes software developed by the
0023: * Walt Disney Internet Group (http://opensource.go.com/)."
0024: * Alternately, this acknowledgment may appear in the software itself,
0025: * if and wherever such third-party acknowledgments normally appear.
0026: *
0027: * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
0028: * not be used to endorse or promote products derived from this
0029: * software without prior written permission. For written
0030: * permission, please contact opensource@dig.com.
0031: *
0032: * 5. Products derived from this software may not be called "Tea",
0033: * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
0034: * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
0035: * written permission of the Walt Disney Internet Group.
0036: *
0037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0040: * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
0041: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0042: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0043: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0044: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
0045: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0046: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0047: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0048: * ====================================================================
0049: *
0050: * For more information about Tea, please see http://opensource.go.com/.
0051: */
0052:
0053: package com.go.tea.runtime;
0054:
0055: import java.text.NumberFormat;
0056: import java.text.DecimalFormat;
0057: import java.text.DecimalFormatSymbols;
0058: import java.util.Date;
0059: import java.util.Map;
0060: import java.util.HashMap;
0061: import java.util.Set;
0062: import java.util.Iterator;
0063: import java.util.Collections;
0064: import java.util.TimeZone;
0065: import java.util.Locale;
0066: import java.beans.PropertyDescriptor;
0067: import java.beans.IntrospectionException;
0068: import com.go.tea.util.BeanAnalyzer;
0069: import com.go.trove.util.Pair;
0070: import com.go.trove.util.FastDateFormat;
0071:
0072: /******************************************************************************
0073: * The default runtime context class that Tea templates get compiled to use.
0074: * All functions callable from a template are defined in the context. To add
0075: * more or override existing ones, do so when extending this class.
0076: *
0077: * @author Brian S O'Neill
0078: * @version
0079: * <!--$$Revision:--> 30 <!-- $-->, <!--$$JustDate:--> 01/07/03 <!-- $-->
0080: */
0081: public abstract class DefaultContext implements UtilityContext {
0082:
0083: private static final String DEFAULT_NULL_FORMAT = "null";
0084:
0085: // Although the Integer.toString method keeps getting more optimized
0086: // with each release, it still isn't very fast at converting small values.
0087: private static final String[] INT_VALUES = { "0", "1", "2", "3",
0088: "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14",
0089: "15", "16", "17", "18", "19", "20", "21", "22", "23", "24",
0090: "25", "26", "27", "28", "29", "30", "31", "32", "33", "34",
0091: "35", "36", "37", "38", "39", "40", "41", "42", "43", "44",
0092: "45", "46", "47", "48", "49", "50", "51", "52", "53", "54",
0093: "55", "56", "57", "58", "59", "60", "61", "62", "63", "64",
0094: "65", "66", "67", "68", "69", "70", "71", "72", "73", "74",
0095: "75", "76", "77", "78", "79", "80", "81", "82", "83", "84",
0096: "85", "86", "87", "88", "89", "90", "91", "92", "93", "94",
0097: "95", "96", "97", "98", "99", };
0098:
0099: private static final int FIRST_INT_VALUE = 0;
0100: private static final int LAST_INT_VALUE = 99;
0101:
0102: private static Map cLocaleCache;
0103: private static Map cNFormatCache;
0104:
0105: static {
0106: cLocaleCache = Collections.synchronizedMap(new HashMap(7));
0107: cNFormatCache = Collections.synchronizedMap(new HashMap(47));
0108: }
0109:
0110: private Locale mLocale;
0111: private String mNullFormat = DEFAULT_NULL_FORMAT;
0112: private FastDateFormat mDateFormat;
0113: private NFormat mNFormat;
0114:
0115: public DefaultContext() {
0116: }
0117:
0118: /**
0119: * Method that is the runtime receiver. Implementations should call one
0120: * of the toString methods when converting this object to a string.
0121: * <p>
0122: * NOTE: This method should <b>not</b> be called directly within a
0123: * template.
0124: *
0125: * @see com.go.tea.compiler.Compiler#getRuntimeReceiver
0126: * @hidden
0127: */
0128: public abstract void print(Object obj) throws Exception;
0129:
0130: /**
0131: * @hidden
0132: */
0133: public void print(Date date) throws Exception {
0134: print(toString(date));
0135: }
0136:
0137: /**
0138: * @hidden
0139: */
0140: public void print(Number n) throws Exception {
0141: print(toString(n));
0142: }
0143:
0144: /**
0145: * @hidden
0146: */
0147: public void print(int n) throws Exception {
0148: print(toString(n));
0149: }
0150:
0151: /**
0152: * @hidden
0153: */
0154: public void print(float n) throws Exception {
0155: print(toString(n));
0156: }
0157:
0158: /**
0159: * @hidden
0160: */
0161: public void print(long n) throws Exception {
0162: print(toString(n));
0163: }
0164:
0165: /**
0166: * @hidden
0167: */
0168: public void print(double n) throws Exception {
0169: print(toString(n));
0170: }
0171:
0172: /**
0173: * @hidden
0174: */
0175: public String toString(Object obj) {
0176: if (obj == null) {
0177: return mNullFormat;
0178: } else if (obj instanceof String) {
0179: return (String) obj;
0180: } else if (obj instanceof Date) {
0181: return toString((Date) obj);
0182: } else if (obj instanceof Number) {
0183: return toString((Number) obj);
0184: } else {
0185: String str = obj.toString();
0186: return (str == null) ? mNullFormat : str;
0187: }
0188: }
0189:
0190: /**
0191: * @hidden
0192: */
0193: public String toString(String str) {
0194: return (str == null) ? mNullFormat : str;
0195: }
0196:
0197: /**
0198: * @hidden
0199: */
0200: public String toString(Date date) {
0201: if (date == null) {
0202: return mNullFormat;
0203: }
0204:
0205: if (mDateFormat == null) {
0206: dateFormat(null);
0207: }
0208:
0209: return mDateFormat.format(date);
0210: }
0211:
0212: /**
0213: * @hidden
0214: */
0215: public String toString(Number n) {
0216: if (n == null) {
0217: return mNullFormat;
0218: } else if (mNFormat == null) {
0219: if (n instanceof Integer) {
0220: return toString(((Integer) n).intValue());
0221: } else if (n instanceof Long) {
0222: return toString(((Long) n).longValue());
0223: } else {
0224: return n.toString();
0225: }
0226: } else {
0227: return mNFormat.format(n);
0228: }
0229: }
0230:
0231: /**
0232: * @hidden
0233: */
0234: public String toString(int n) {
0235: if (mNFormat == null) {
0236: if (n <= LAST_INT_VALUE && n >= FIRST_INT_VALUE) {
0237: return INT_VALUES[n];
0238: } else {
0239: return Integer.toString(n);
0240: }
0241: } else {
0242: return mNFormat.format(n);
0243: }
0244: }
0245:
0246: /**
0247: * @hidden
0248: */
0249: public String toString(float n) {
0250: return (mNFormat == null) ? Float.toString(n) : mNFormat
0251: .format(n);
0252: }
0253:
0254: /**
0255: * @hidden
0256: */
0257: public String toString(long n) {
0258: if (mNFormat == null) {
0259: if (n <= LAST_INT_VALUE && n >= FIRST_INT_VALUE) {
0260: return INT_VALUES[(int) n];
0261: } else {
0262: return Long.toString(n);
0263: }
0264: } else {
0265: return mNFormat.format(n);
0266: }
0267: }
0268:
0269: /**
0270: * @hidden
0271: */
0272: public String toString(double n) {
0273: return (mNFormat == null) ? Double.toString(n) : mNFormat
0274: .format(n);
0275: }
0276:
0277: public void setLocale(Locale locale) {
0278: if (locale == null) {
0279: mLocale = null;
0280: mDateFormat = null;
0281: mNFormat = null;
0282: } else {
0283: synchronized (cLocaleCache) {
0284: Locale cached = (Locale) cLocaleCache.get(locale);
0285: if (cached == null) {
0286: cLocaleCache.put(locale, locale);
0287: } else {
0288: locale = cached;
0289: }
0290: }
0291:
0292: mLocale = locale;
0293: dateFormat(null);
0294: numberFormat(null);
0295: }
0296: }
0297:
0298: public void setLocale(String language, String country) {
0299: setLocale(new Locale(language, country));
0300: }
0301:
0302: public void setLocale(String language, String country,
0303: String variant) {
0304: setLocale(new Locale(language, country, variant));
0305: }
0306:
0307: public java.util.Locale getLocale() {
0308: return mLocale;
0309: }
0310:
0311: public Locale[] getAvailableLocales() {
0312: return Locale.getAvailableLocales();
0313: }
0314:
0315: public void nullFormat(String format) {
0316: mNullFormat = (format == null) ? DEFAULT_NULL_FORMAT : format;
0317: }
0318:
0319: public String getNullFormat() {
0320: return mNullFormat;
0321: }
0322:
0323: public void dateFormat(String format) {
0324: dateFormat(format, null);
0325: }
0326:
0327: public void dateFormat(String format, String timeZoneID) {
0328: TimeZone timeZone;
0329: if (timeZoneID != null) {
0330: timeZone = TimeZone.getTimeZone(timeZoneID);
0331: } else {
0332: timeZone = null;
0333: }
0334:
0335: if (format == null) {
0336: mDateFormat = FastDateFormat.getDateTimeInstance(
0337: FastDateFormat.LONG, FastDateFormat.LONG, timeZone,
0338: mLocale);
0339: } else {
0340: mDateFormat = FastDateFormat.getInstance(format, timeZone,
0341: mLocale);
0342: }
0343: }
0344:
0345: public String getDateFormat() {
0346: if (mDateFormat == null) {
0347: dateFormat(null);
0348: }
0349: return mDateFormat.getPattern();
0350: }
0351:
0352: public String getDateFormatTimeZone() {
0353: if (mDateFormat == null) {
0354: dateFormat(null);
0355: }
0356: TimeZone timeZone = mDateFormat.getTimeZone();
0357: return timeZone == null ? null : timeZone.getID();
0358: }
0359:
0360: public TimeZone[] getAvailableTimeZones() {
0361: String[] IDs = TimeZone.getAvailableIDs();
0362: TimeZone[] zones = new TimeZone[IDs.length];
0363: for (int i = zones.length; --i >= 0;) {
0364: zones[i] = TimeZone.getTimeZone(IDs[i]);
0365: }
0366: return zones;
0367: }
0368:
0369: public void numberFormat(String format) {
0370: numberFormat(format, null, null);
0371: }
0372:
0373: public void numberFormat(String format, String infinity, String NaN) {
0374: if (format == null && infinity == null && NaN == null) {
0375: if (mLocale == null) {
0376: mNFormat = null;
0377: } else {
0378: mNFormat = new NFormat(NumberFormat
0379: .getNumberInstance(mLocale));
0380: }
0381: return;
0382: }
0383:
0384: Object key;
0385: if (mLocale == null) {
0386: key = format;
0387: } else {
0388: key = new Pair(format, mLocale);
0389: }
0390:
0391: if (infinity != null || NaN != null) {
0392: key = new Pair(key, infinity);
0393: key = new Pair(key, NaN);
0394: }
0395:
0396: if ((mNFormat = (NFormat) cNFormatCache.get(key)) == null) {
0397:
0398: DecimalFormat df;
0399:
0400: if (mLocale == null) {
0401: if (format == null) {
0402: df = new DecimalFormat();
0403: } else {
0404: df = new DecimalFormat(format);
0405: }
0406: } else {
0407: DecimalFormatSymbols symbols = new DecimalFormatSymbols(
0408: mLocale);
0409: if (format == null) {
0410: df = new DecimalFormat();
0411: df.setDecimalFormatSymbols(symbols);
0412: } else {
0413: df = new DecimalFormat(format, symbols);
0414: }
0415: }
0416:
0417: if (infinity != null || NaN != null) {
0418: DecimalFormatSymbols symbols = df
0419: .getDecimalFormatSymbols();
0420: symbols = (DecimalFormatSymbols) symbols.clone();
0421: if (infinity != null) {
0422: symbols.setInfinity(infinity);
0423: }
0424: if (NaN != null) {
0425: symbols.setNaN(NaN);
0426: }
0427: df.setDecimalFormatSymbols(symbols);
0428: }
0429:
0430: mNFormat = new NFormat(df);
0431: cNFormatCache.put(key, mNFormat);
0432: }
0433: }
0434:
0435: public String getNumberFormat() {
0436: return mNFormat == null ? null : mNFormat.getNumberFormat();
0437: }
0438:
0439: public String getNumberFormatInfinity() {
0440: return mNFormat == null ? null : mNFormat
0441: .getNumberFormatInfinity();
0442: }
0443:
0444: public String getNumberFormatNaN() {
0445: return mNFormat == null ? null : mNFormat.getNumberFormatNaN();
0446: }
0447:
0448: public Date currentDate() {
0449: return new Date();
0450: }
0451:
0452: public boolean startsWith(String str, String prefix) {
0453: return (str == null || prefix == null) ? (str == prefix) : str
0454: .startsWith(prefix);
0455: }
0456:
0457: public boolean endsWith(String str, String suffix) {
0458: return (str == null || suffix == null) ? (str == suffix) : str
0459: .endsWith(suffix);
0460: }
0461:
0462: public int[] find(String str, String search) {
0463: return find(str, search, 0);
0464: }
0465:
0466: public int[] find(String str, String search, int fromIndex) {
0467: if (str == null || search == null) {
0468: return new int[0];
0469: }
0470:
0471: int[] indices = new int[10];
0472: int size = 0;
0473:
0474: int index = fromIndex;
0475: while ((index = str.indexOf(search, index)) >= 0) {
0476: if (size >= indices.length) {
0477: // Expand capacity.
0478: int[] newArray = new int[indices.length * 2];
0479: System.arraycopy(indices, 0, newArray, 0,
0480: indices.length);
0481: indices = newArray;
0482: }
0483: indices[size++] = index;
0484: index += search.length();
0485: }
0486:
0487: if (size < indices.length) {
0488: // Trim capacity.
0489: int[] newArray = new int[size];
0490: System.arraycopy(indices, 0, newArray, 0, size);
0491: indices = newArray;
0492: }
0493:
0494: return indices;
0495: }
0496:
0497: public int findFirst(String str, String search) {
0498: return (str == null || search == null) ? -1 : str
0499: .indexOf(search);
0500: }
0501:
0502: public int findFirst(String str, String search, int fromIndex) {
0503: return (str == null || search == null) ? -1 : str.indexOf(
0504: search, fromIndex);
0505: }
0506:
0507: public int findLast(String str, String search) {
0508: return (str == null || search == null) ? -1 : str
0509: .lastIndexOf(search);
0510: }
0511:
0512: public int findLast(String str, String search, int fromIndex) {
0513: return (str == null || search == null) ? -1 : str.lastIndexOf(
0514: search, fromIndex);
0515: }
0516:
0517: public String substring(String str, int startIndex) {
0518: return (str == null) ? null : str.substring(startIndex);
0519: }
0520:
0521: public String substring(String str, int startIndex, int endIndex) {
0522: return (str == null) ? null : str.substring(startIndex,
0523: endIndex);
0524: }
0525:
0526: public String toLowerCase(String str) {
0527: return (str == null) ? null : str.toLowerCase();
0528: }
0529:
0530: public String toUpperCase(String str) {
0531: return (str == null) ? null : str.toUpperCase();
0532: }
0533:
0534: public String trim(String str) {
0535: return (str == null) ? null : str.trim();
0536: }
0537:
0538: public String trimLeading(String str) {
0539: if (str == null) {
0540: return null;
0541: }
0542:
0543: int length = str.length();
0544: for (int i = 0; i < length; i++) {
0545: if (str.charAt(i) > ' ') {
0546: return str.substring(i);
0547: }
0548: }
0549:
0550: return "";
0551: }
0552:
0553: public String trimTrailing(String str) {
0554: if (str == null) {
0555: return null;
0556: }
0557:
0558: int length = str.length();
0559: for (int i = length - 1; i >= 0; i--) {
0560: if (str.charAt(i) > ' ') {
0561: return str.substring(0, i + 1);
0562: }
0563: }
0564:
0565: return "";
0566: }
0567:
0568: public String replace(String source, String pattern,
0569: String replacement) {
0570: return replace(source, pattern, replacement, 0);
0571: }
0572:
0573: public String replace(String source, String pattern,
0574: String replacement, int fromIndex) {
0575: if (source == null) {
0576: if (pattern == null) {
0577: return replacement;
0578: } else {
0579: return source;
0580: }
0581: }
0582:
0583: int patternLength;
0584: if (pattern == null || (patternLength = pattern.length()) == 0) {
0585: return source;
0586: }
0587:
0588: if (replacement == null) {
0589: replacement = toString(replacement);
0590: }
0591:
0592: int sourceLength = source.length();
0593:
0594: StringBuffer buf;
0595: if (fromIndex <= 0) {
0596: fromIndex = 0;
0597: buf = new StringBuffer(sourceLength);
0598: } else if (fromIndex < sourceLength) {
0599: buf = new StringBuffer(sourceLength);
0600: buf.append(source.substring(0, fromIndex));
0601: } else {
0602: return source;
0603: }
0604:
0605: sourceScan: for (int s = fromIndex; s < sourceLength;) {
0606: int k = s;
0607: for (int j = 0; j < patternLength; j++, k++) {
0608: if (k >= sourceLength
0609: || source.charAt(k) != pattern.charAt(j)) {
0610:
0611: buf.append(source.charAt(s));
0612: s++;
0613: continue sourceScan;
0614: }
0615: }
0616:
0617: buf.append(replacement);
0618: s = k;
0619: }
0620:
0621: return buf.toString();
0622: }
0623:
0624: public String replace(String source, Map patternReplacements) {
0625: if (source == null) {
0626: return null;
0627: }
0628:
0629: int mapSize = patternReplacements.size();
0630: String[] patterns = new String[mapSize];
0631: String[] replacements = new String[mapSize];
0632:
0633: Iterator it = patternReplacements.entrySet().iterator();
0634: for (int i = 0; it.hasNext(); i++) {
0635: Map.Entry entry = (Map.Entry) it.next();
0636:
0637: patterns[i] = toString(entry.getKey());
0638: replacements[i] = toString(entry.getValue());
0639: }
0640:
0641: return replace(source, patterns, replacements);
0642: }
0643:
0644: private static String replace(String source, String[] patterns,
0645: String[] replacements) {
0646: int patternsLength = patterns.length;
0647:
0648: int sourceLength = source.length();
0649: StringBuffer buf = new StringBuffer(sourceLength);
0650:
0651: for (int s = 0; s < sourceLength;) {
0652: int longestPattern = 0;
0653: int closestPattern = -1;
0654:
0655: patternScan: for (int i = 0; i < patternsLength; i++) {
0656: String pattern = patterns[i];
0657: int patternLength = pattern.length();
0658:
0659: if (patternLength > 0) {
0660: for (int j = 0, k = s; j < patternLength; j++, k++) {
0661: if (k >= sourceLength
0662: || source.charAt(k) != pattern
0663: .charAt(j)) {
0664:
0665: continue patternScan;
0666: }
0667: }
0668:
0669: if (patternLength > longestPattern) {
0670: longestPattern = patternLength;
0671: closestPattern = i;
0672: }
0673: }
0674: }
0675:
0676: if (closestPattern >= 0) {
0677: buf.append(replacements[closestPattern]);
0678: s += longestPattern;
0679: } else {
0680: buf.append(source.charAt(s));
0681: s++;
0682: }
0683: }
0684:
0685: return buf.toString();
0686: }
0687:
0688: public String replaceFirst(String source, String pattern,
0689: String replacement) {
0690: return replaceOne(source, pattern, replacement, findFirst(
0691: source, pattern));
0692: }
0693:
0694: public String replaceFirst(String source, String pattern,
0695: String replacement, int fromIndex) {
0696: return replaceOne(source, pattern, replacement, findFirst(
0697: source, pattern, fromIndex));
0698: }
0699:
0700: public String replaceLast(String source, String pattern,
0701: String replacement) {
0702: return replaceOne(source, pattern, replacement, findLast(
0703: source, pattern));
0704: }
0705:
0706: public String replaceLast(String source, String pattern,
0707: String replacement, int fromIndex) {
0708: return replaceOne(source, pattern, replacement, findLast(
0709: source, pattern, fromIndex));
0710: }
0711:
0712: private String replaceOne(String source, String pattern,
0713: String replacement, int atIndex) {
0714: if (atIndex < 0) {
0715: if (source == null && pattern == null) {
0716: return replacement;
0717: } else {
0718: return source;
0719: }
0720: }
0721:
0722: if (replacement == null) {
0723: replacement = toString(replacement);
0724: }
0725:
0726: StringBuffer buf = new StringBuffer(source.length()
0727: - pattern.length() + replacement.length());
0728: buf.append(source.substring(0, atIndex));
0729: buf.append(replacement);
0730: buf.append(source.substring(atIndex + pattern.length()));
0731:
0732: return buf.toString();
0733: }
0734:
0735: public String shortOrdinal(Long n) {
0736: return (n == null) ? null : shortOrdinal(n.longValue());
0737: }
0738:
0739: /**
0740: * @hidden
0741: */
0742: public String shortOrdinal(long n) {
0743: String str = Long.toString(n);
0744:
0745: if (n < 0) {
0746: n = -n;
0747: }
0748:
0749: n %= 100;
0750:
0751: if (n >= 10 && n <= 20) {
0752: str += "th";
0753: } else {
0754: if (n > 20)
0755: n %= 10;
0756:
0757: switch ((int) n) {
0758: case 1:
0759: str += "st";
0760: break;
0761: case 2:
0762: str += "nd";
0763: break;
0764: case 3:
0765: str += "rd";
0766: break;
0767: default:
0768: str += "th";
0769: break;
0770: }
0771: }
0772:
0773: return str;
0774: }
0775:
0776: public String ordinal(Long n) {
0777: return (n == null) ? null : ordinal(n.longValue());
0778: }
0779:
0780: /**
0781: * @hidden
0782: */
0783: public String ordinal(long n) {
0784: if (n == 0) {
0785: return "zeroth";
0786: }
0787:
0788: StringBuffer buf = new StringBuffer(20);
0789:
0790: if (n < 0) {
0791: buf.append("negative ");
0792: n = -n;
0793: }
0794:
0795: n = cardinalGroup(buf, n, 1000000000000000000L, "quintillion");
0796: n = cardinalGroup(buf, n, 1000000000000000L, "quadrillion");
0797: n = cardinalGroup(buf, n, 1000000000000L, "trillion");
0798: n = cardinalGroup(buf, n, 1000000000L, "billion");
0799: n = cardinalGroup(buf, n, 1000000L, "million");
0800: n = cardinalGroup(buf, n, 1000L, "thousand");
0801:
0802: if (n == 0) {
0803: buf.append("th");
0804: } else {
0805: cardinal999(buf, n, true);
0806: }
0807:
0808: return buf.toString();
0809: }
0810:
0811: public String cardinal(Long n) {
0812: return (n == null) ? null : cardinal(n.longValue());
0813: }
0814:
0815: /**
0816: * @hidden
0817: */
0818: public String cardinal(long n) {
0819: if (n == 0) {
0820: return "zero";
0821: }
0822:
0823: StringBuffer buf = new StringBuffer(20);
0824:
0825: if (n < 0) {
0826: buf.append("negative ");
0827: n = -n;
0828: }
0829:
0830: n = cardinalGroup(buf, n, 1000000000000000000L, "quintillion");
0831: n = cardinalGroup(buf, n, 1000000000000000L, "quadrillion");
0832: n = cardinalGroup(buf, n, 1000000000000L, "trillion");
0833: n = cardinalGroup(buf, n, 1000000000L, "billion");
0834: n = cardinalGroup(buf, n, 1000000L, "million");
0835: n = cardinalGroup(buf, n, 1000L, "thousand");
0836:
0837: cardinal999(buf, n, false);
0838:
0839: return buf.toString();
0840: }
0841:
0842: private static long cardinalGroup(StringBuffer buf, long n,
0843: long threshold, String groupName) {
0844: if (n >= threshold) {
0845: cardinal999(buf, n / threshold, false);
0846: buf.append(' ');
0847: buf.append(groupName);
0848: n %= threshold;
0849: if (n >= 100) {
0850: buf.append(", ");
0851: } else if (n != 0) {
0852: buf.append(" and ");
0853: }
0854: }
0855:
0856: return n;
0857: }
0858:
0859: private static void cardinal999(StringBuffer buf, long n,
0860: boolean ordinal) {
0861: n = cardinalGroup(buf, n, 100L, "hundred");
0862:
0863: if (n == 0) {
0864: if (ordinal) {
0865: buf.append("th");
0866: }
0867: return;
0868: }
0869:
0870: if (n >= 20) {
0871: switch ((int) n / 10) {
0872: case 2:
0873: buf.append("twen");
0874: break;
0875: case 3:
0876: buf.append("thir");
0877: break;
0878: case 4:
0879: buf.append("for");
0880: break;
0881: case 5:
0882: buf.append("fif");
0883: break;
0884: case 6:
0885: buf.append("six");
0886: break;
0887: case 7:
0888: buf.append("seven");
0889: break;
0890: case 8:
0891: buf.append("eigh");
0892: break;
0893: case 9:
0894: buf.append("nine");
0895: break;
0896: }
0897:
0898: n %= 10;
0899: if (n != 0) {
0900: buf.append("ty-");
0901: } else {
0902: if (!ordinal) {
0903: buf.append("ty");
0904: } else {
0905: buf.append("tieth");
0906: }
0907: }
0908: }
0909:
0910: switch ((int) n) {
0911: case 1:
0912: if (!ordinal) {
0913: buf.append("one");
0914: } else {
0915: buf.append("first");
0916: }
0917: break;
0918: case 2:
0919: if (!ordinal) {
0920: buf.append("two");
0921: } else {
0922: buf.append("second");
0923: }
0924: break;
0925: case 3:
0926: if (!ordinal) {
0927: buf.append("three");
0928: } else {
0929: buf.append("third");
0930: }
0931: break;
0932: case 4:
0933: if (!ordinal) {
0934: buf.append("four");
0935: } else {
0936: buf.append("fourth");
0937: }
0938: break;
0939: case 5:
0940: if (!ordinal) {
0941: buf.append("five");
0942: } else {
0943: buf.append("fifth");
0944: }
0945: break;
0946: case 6:
0947: if (!ordinal) {
0948: buf.append("six");
0949: } else {
0950: buf.append("sixth");
0951: }
0952: break;
0953: case 7:
0954: if (!ordinal) {
0955: buf.append("seven");
0956: } else {
0957: buf.append("seventh");
0958: }
0959: break;
0960: case 8:
0961: if (!ordinal) {
0962: buf.append("eight");
0963: } else {
0964: buf.append("eighth");
0965: }
0966: break;
0967: case 9:
0968: if (!ordinal) {
0969: buf.append("nine");
0970: } else {
0971: buf.append("ninth");
0972: }
0973: break;
0974: case 10:
0975: if (!ordinal) {
0976: buf.append("ten");
0977: } else {
0978: buf.append("tenth");
0979: }
0980: break;
0981: case 11:
0982: if (!ordinal) {
0983: buf.append("eleven");
0984: } else {
0985: buf.append("eleventh");
0986: }
0987: break;
0988: case 12:
0989: if (!ordinal) {
0990: buf.append("twelve");
0991: } else {
0992: buf.append("twelfth");
0993: }
0994: break;
0995: case 13:
0996: buf.append("thirteen");
0997: if (ordinal)
0998: buf.append("th");
0999: break;
1000: case 14:
1001: buf.append("fourteen");
1002: if (ordinal)
1003: buf.append("th");
1004: break;
1005: case 15:
1006: buf.append("fifteen");
1007: if (ordinal)
1008: buf.append("th");
1009: break;
1010: case 16:
1011: buf.append("sixteen");
1012: if (ordinal)
1013: buf.append("th");
1014: break;
1015: case 17:
1016: buf.append("seventeen");
1017: if (ordinal)
1018: buf.append("th");
1019: break;
1020: case 18:
1021: buf.append("eighteen");
1022: if (ordinal)
1023: buf.append("th");
1024: break;
1025: case 19:
1026: buf.append("nineteen");
1027: if (ordinal)
1028: buf.append("th");
1029: break;
1030: }
1031: }
1032:
1033: private static class NFormat {
1034: private final NumberFormat mFormat;
1035: private final boolean mFormatInteger;
1036:
1037: public NFormat(NumberFormat format) {
1038: mFormat = format;
1039: boolean formatInteger = true;
1040:
1041: if (format instanceof DecimalFormat) {
1042: DecimalFormat dformat = (DecimalFormat) format;
1043:
1044: // Check if the number format actually does anything special
1045: // for integers.
1046:
1047: formatInteger = dformat.isDecimalSeparatorAlwaysShown()
1048: || dformat.isGroupingUsed()
1049: || dformat.getMinimumIntegerDigits() > 1
1050: || dformat.getMinimumFractionDigits() > 0
1051: || dformat.getPositiveSuffix().length() > 0
1052: || dformat.getPositivePrefix().length() > 0
1053: || dformat.getNegativeSuffix().length() > 0
1054: || (!("-".equals(dformat.getNegativePrefix())));
1055:
1056: if (!formatInteger) {
1057: formatInteger = dformat.getDecimalFormatSymbols()
1058: .getZeroDigit() != '0';
1059: }
1060: }
1061:
1062: mFormatInteger = formatInteger;
1063: }
1064:
1065: public String format(int value) {
1066: return (!mFormatInteger) ? Integer.toString(value)
1067: : mFormat.format(value);
1068: }
1069:
1070: public String format(float value) {
1071: return mFormat.format(value);
1072: }
1073:
1074: public String format(long value) {
1075: return (!mFormatInteger) ? Long.toString(value) : mFormat
1076: .format(value);
1077: }
1078:
1079: public String format(double value) {
1080: return mFormat.format(value);
1081: }
1082:
1083: public String format(Number value) {
1084: if (value instanceof Integer) {
1085: return format(((Integer) value).intValue());
1086: } else if (value instanceof Long) {
1087: return format(((Long) value).longValue());
1088: } else {
1089: return mFormat.format(value);
1090: }
1091: }
1092:
1093: public String getNumberFormat() {
1094: if (mFormat instanceof DecimalFormat) {
1095: return ((DecimalFormat) mFormat).toPattern();
1096: }
1097: return null;
1098: }
1099:
1100: public String getNumberFormatInfinity() {
1101: if (mFormat instanceof DecimalFormat) {
1102: DecimalFormatSymbols symbols = ((DecimalFormat) mFormat)
1103: .getDecimalFormatSymbols();
1104: return symbols.getInfinity();
1105: }
1106: return null;
1107: }
1108:
1109: public String getNumberFormatNaN() {
1110: if (mFormat instanceof DecimalFormat) {
1111: DecimalFormatSymbols symbols = ((DecimalFormat) mFormat)
1112: .getDecimalFormatSymbols();
1113: return symbols.getNaN();
1114: }
1115: return null;
1116: }
1117: }
1118: }
|